概念
线程 : 是一个程序内部的顺序控制流。通俗的来说,是一个程序里面不同的执行路径 (记住这句话就可以了)
程序: wechat.exe
进程: wechat.exe启动后,叫做一个进程,是相对于程序来说的,是个动态的概念
线程: 作为一个进程里面最小的执行单元就叫一个线程,或者说,一个程序里不同的执行路径就叫做一个线程
平时所讲的进程开始执行了,是指进程中的主线程(main方法)开始执行了
Java的线程是通过Java.lang.Thread
来实现的
VM启动时会有一个由主方法(public static void main(){}
)所sing以的线程
可以通过创建Thread
类的实例来创建新的线程
每个线程都是通过某个特定的Thread
对象所对应的run()
来完成其操作的,方法run()
称为线程体。
通过调用Thread类的start方法来启动一个线程
线程的创建和启动
定义线程类实现Runnable
接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class Runner1 implements Runnable { @Override public void run () { } public static void main (String[] args) { Runner1 runner1 = new Runner1(); Thread t = new Thread(runner1); t.start(); } }
继承Thread
类
1 2 3 4 5 6 7 8 9 10 11 public class Thread1 extends Thread { @Override public void run () { super .run(); } public static void main (String[] args) { Thread1 thread1 = new Thread1(); thread1.start(); } }
线程的状态
线程同步
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 package com.shifpeng.scaffold.test;public class TestSync implements Runnable { Timer timer = new Timer(); public static void main (String[] args) { TestSync testSync = new TestSync(); Thread th1 = new Thread(testSync); Thread th2 = new Thread(testSync); th1.setName("t1" ); th2.setName("t2" ); th1.start(); th2.start(); } @Override public void run () { timer.add(Thread.currentThread().getName()); } } class Timer { private static int num = 0 ; public void add (String name) { num++; try { Thread.sleep(1 ); } catch (InterruptedException e) { e.printStackTrace(); } System.out.printf(name + "你是第" + num + "个使用Timer的线程\n" ); } }
打印结果
1 2 t1 你是第2 个使用Timer的线程t2 你是第2 个使用Timer的线程
这个小程序执行出来的效果明显不对,原因就是线程在执行过程中,被另外一个线程打断了,两个线程应该作为原子性的输出
如何解决?在执行当前过程中,锁住该线程:
锁定当前对象synchronized(this)
在Java语言中,引入了对象互斥锁的概念,保证共享数据操作的完整性,保证在一个时刻,只能有一个线程访问该对象
对上面的小程序做相应修改
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Timer { private static int num = 0 ; public void add (String name) { synchronized (this ) { num++; try { Thread.sleep(1 ); } catch (InterruptedException e) { e.printStackTrace(); } System.out.printf(name + "你是第" + num + "个使用Timer的线程\n" ); } } }
另一种简便的写法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class Timer { private static int num = 0 ; public synchronized void add (String name) { num++; try { Thread.sleep(1 ); } catch (InterruptedException e) { e.printStackTrace(); } System.out.printf(name + "你是第" + num + "个使用Timer的线程\n" ); } }
死锁
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 package com.shifpeng.scaffold.test;public class TestDeadLock implements Runnable { public int flag = 1 ; static Object o1 = new Object(), o2 = new Object(); @Override public void run () { System.out.printf("flag=" + flag + "\n" ); if (flag == 1 ) { synchronized (o1) { try { Thread.sleep(500 ); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (o2) { System.out.printf("1" + "\n" ); } } } if (flag == 0 ) { synchronized (o2) { try { Thread.sleep(500 ); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (o1) { System.out.println("0" + "\n" ); } } } } public static void main (String[] args) { TestDeadLock testDeadLock1 = new TestDeadLock(); TestDeadLock testDeadLock2 = new TestDeadLock(); testDeadLock1.flag = 1 ; testDeadLock1.flag = 0 ; Thread th1 = new Thread(testDeadLock1); Thread th2 = new Thread(testDeadLock2); th1.start(); th2.start(); } } 程序执行到下面这里,就卡住不动了
面试题 :下面的程序中,m1在执行过程中,m2可以执行吗?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 package com.shifpeng.scaffold.test;public class TT implements Runnable { int b = 100 ; public synchronized void m1 () throws Exception { b = 1000 ; System.out.printf("b=" + b + "\n" ); Thread.sleep(5000 ); System.out.printf("b=" + b + "\n" ); } public void m2 () throws Exception { Thread.sleep(2500 ); b = 2000 ; } @Override public void run () { try { m1(); } catch (Exception e) { e.printStackTrace(); } } public static void main (String[] args) throws Exception { TT tt = new TT(); Thread th1 = new Thread(tt); th1.start(); Thread.sleep(1000 ); tt.m2(); System.out.println(tt.b); } } b=1000 2000 b=2000
上面得出的结论即:
一个对象中的非同步方法,可以被其他线程自由访问,并且可能对同步的方法产生影响
上面的程序加以修改(给m2也加上锁)
1 2 3 4 5 6 7 8 9 10 11 public synchronized void m2 () throws Exception { Thread.sleep(2500 ); b = 2000 ; } b=1000 b=1000 2000
生产者消费者问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 package com.shifpeng.scaffold.test;public class ProducerConsumer { public static void main (String[] args) { SyncStack syncStack = new SyncStack(); Producer producer = new Producer(syncStack); Consumer consumer = new Consumer(syncStack); new Thread(producer).start(); new Thread(consumer).start(); } } class ManTou { int id; ManTou(int id) { this .id = id; } @Override public String toString () { return "馒头:" + this .id; } } class SyncStack { int index = 0 ; ManTou[] manTous = new ManTou[6 ]; public synchronized void push (ManTou mt) { if (index == manTous.length) { try { this .wait(); } catch (InterruptedException e) { e.printStackTrace(); } } this .notify(); manTous[index] = mt; index++; } public synchronized ManTou pop () { if (index == 0 ) { try { this .wait(); } catch (InterruptedException e) { e.printStackTrace(); } } this .notify(); index--; return manTous[index]; } } class Producer implements Runnable { SyncStack syncStack = null ; Producer(SyncStack ss) { this .syncStack = ss; } @Override public void run () { for (int i = 0 ; i < 20 ; i++) { ManTou mt = new ManTou(i); syncStack.push(mt); System.out.println("生产了:" + mt); try { Thread.sleep((long )Math.random()*2 ); } catch (InterruptedException e) { e.printStackTrace(); } } } } class Consumer implements Runnable { SyncStack syncStack = null ; Consumer(SyncStack ss) { this .syncStack = ss; } @Override public void run () { for (int i = 0 ; i < 20 ; i++) { ManTou mt = syncStack.pop(); System.out.println("消费了:" + mt); try { Thread.sleep((long )Math.random()*1000 ); } catch (InterruptedException e) { e.printStackTrace(); } } } }
注意:上面的程序有个潜在性的问题
1 2 3 4 5 6 7 8 9 10 11 12 public synchronized void push (ManTou mt) { if (index == manTous.length) { try { this .wait(); } catch (InterruptedException e) { e.printStackTrace(); } } this .notify(); manTous[index] = mt; index++; }
上面的this.wait();
如果被打断,就会进入到Exception
,然后程序会继续执行,继续++,这个时候需要注意,程序需要做一些调整
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 public synchronized void push (ManTou mt) { while (index == manTous.length) { try { this .wait(); } catch (InterruptedException e) { e.printStackTrace(); } } this .notify(); manTous[index] = mt; index++; } public synchronized ManTou pop () { while (index == 0 ) { try { this .wait(); } catch (InterruptedException e) { e.printStackTrace(); } } this .notify(); index--; return manTous[index]; }
需要将if
换成while
wait
和sleep
的区别
wait是object的方法,sleep是thread的方法
wait
时别的线程可以访问锁定对象
调用wait方法的时候必须锁定该对象
sleep时别的线程不可以访问锁定对象
如果您喜欢此博客或发现它对您有用,则欢迎对此发表评论。 也欢迎您共享此博客,以便更多人可以参与。 如果博客中使用的图像侵犯了您的版权,请与作者联系以将其删除。 谢谢 !