java运行时最起码有两条线程,主线程和垃圾回收线程。
并行:指物理上同时执行。
并发:指能够让多个任务在逻辑上交织执行的程序设计。
进程:正在运行的程序。
线程:进程的一条执行路径。
随机性原理:因为cpu的快速切换造成的,线程会争夺cpu的执行权。
线程的运行代码统一存放在了run方法中,用start方法开启线程。
直接调用run()方法的话,系统只会把线程对象当成一个普通对象,把run()方法也当成一个普通方法,而不是线程执行体。
创建线程的方式:
1.继承Thread类
public class Test01 { public static void main(String[] args) { MyThread mt = new MyThread(); // 4,创建Thread类的子类对象 mt.start(); // 5,开启线程 for (int i = 0; i < 1000; i++) { System.out.println("bb"); } } } class MyThread extends Thread { // 1,继承Thread public void run() { // 2,重写run方法 for (int i = 0; i < 1000; i++) { // 3,将要执行的代码写在run方法中 System.out.println("第一种方式"); } } }
返回当前线程的名称:this.getName()
好处:可以直接使用Thread类中的方法。
弊端:有父类就不能用这种方法(即单继承的弊端)。
2.实现Runnable接口
public class Test02 { public static void main(String[] args) { MyRunnable mr = new MyRunnable(); // 4,创建Runnable的子类对象 Thread t = new Thread(mr); // 5,将其当作参数传递给Thread的构造函数 t.start(); // 6,开启线程 for (int i = 0; i < 1000; i++) { System.out.println("bb"); } } } class MyRunnable implements Runnable { // 1,定义一个类实现Runnable @Override public void run() { // 2,重写run方法 for (int i = 0; i < 1000; i++) { // 3,将要执行的代码写在run方法中 System.out.println("第二种方式"); } } }
返回当前线程的名称:Thread.currentThread().getName()
好处和弊端刚好和继承Thread类相反,两者是互补的。
3.匿名内部类方式
public class Test03 { public static void main(String[] args) { new Thread() { public void run() { for (int i = 0; i < 1000; i++) { System.out.println("AA"); } }; }.start(); new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 1000; i++) { System.out.println("BB"); } } }).start(); } }
控制线程:
1.设置线程的名字:setName()通过构造方法
2.获取名字getName() 3.获取当前线程的对象Thread.currentThread(); 4.让线程休眠指定的时间Thread.sleep(毫秒值) 5.守护线程setDaemon(true): 把一个线程设置为守护线程(当其他线程都结束了,它如果没有执行完毕也会结束.) 6.让线程插队:join(): 让指定的线程优先执行,然后其他的线程再执行 7.礼让线程:yield(): 让出CPU执行权. 8.设置线程的优先级:线程优先级从: 1-10 默认的线程优先级: 5 setPriority()
线程安全问题(常用同步代码块):
由于多个线程在操作共享数据,且多条语句对共享数据进行运算,所以产生了多线程安全问题,从而有了同步代码块。
同步的前提:
1.至少两个线程。
2.必须保证是使用同一个锁。
补充:多次start()同一个线程是非法的。
同步代码块和同步函数的特点?
1.同步代码块使用的锁可以是任意对象(但不能用匿名对象)。
2.同步函数(在方法上加synchronized修饰就行)使用的锁是this,静态同步函数的锁是该类的字节码文件对象。
在一个类中只有一个同步,可以使用同步函数。如果有多同步,必须使用同步代码块,来确定不同的锁。所以同步代码块相对灵活一些。
死锁:就是同步方法中有同步代码块,或反之。
线程间的通信(常用同步函数):
思路:多个线程在操作同一个资源,但是操作的动作却不一样。
1.将资源封装成对象。
2.将线程执行的任务(任务其实就是run方法)也封装成对象。
等待唤醒机制:
wait:将同步中的线程处于冻结状态。释放了执行权,释放了资格。同时将线程对象存储到线程池中。
notify:唤醒线程池中某一个等待线程。
notifyAll:唤醒的是线程池中的所有线程。
wait和sleep区别:
wait:可以指定时间也可以不指定时间。不指定时间,只能由对应的notify或者notifyAll来唤醒。
sleep:必须指定时间,时间到自动从冻结状态转成运行状态(临时阻塞状态)。
wait:线程会释放执行权,而且线程会释放锁。
sleep:线程会释放执行权,但是不释放锁。
同步锁synchronized和互斥锁 ReentrantLock 的区别:
1.同步是隐示的锁操作,而Lock对象是显示的锁操作。
2.互斥锁 ReentrantLock—java5 三个或者以上通信(推荐使用)。
例子:
ReentrantLock lck = new ReentrantLock();//创建一个互斥锁对象lck.lock(): 获取锁对象 //被同步的内容 lck.unlock();//释放锁对象 //监视器 Condition c = lck.newCondition();//获取监视器对象 c.signal();//唤醒等待中的线程 signalAll()唤醒所有 c.await();//让线程等待.
线程的状态:
线程的停止:让线程运行的代码结束,也就是结束run方法。
怎么结束run方法?一般run方法里肯定定义循环。所以只要结束循环即可。
1.定义循环的结束标记。
2.如果线程处于了冻结状态,是不可能读到标记的。这时就需要通过Thread类中的interrupt方法,将其冻结状态强制清除。让线程恢复具备执行资格的状态,让线程可以读到标记,并结束。
线程池:
A:线程池概述:
程序启动一个新线程成本是比较高的,因为它涉及到要与操作系统进行交互,而使用线程池可以很好的提高性能。
例如:常量池中会存储-128~127之间的Integer对象原理一样
例子(略)
在静态方法上使用同步时会发生什么事?
同步静态方法时会获取该类的“Class”对象,所以当一个线程进入同步的静态方法中时,线程监视器获取类本身的对象锁,其它线程不能进入这个类的任何静态同步方法。它不像实例方法,因为多个线程可以同时访问不同实例同步实例方法。
当一个同步方法已经执行,线程能够调用对象上的非同步实例方法吗?
可以,一个非同步方法总是可以被调用而不会有任何问题。实际上,Java没有为非同步方法做任何检查,锁对象仅仅在同步方法或者同步代码块中检查。
如果一个方法没有声明为同步,即使你在使用共享数据Java照样会调用,而不会做检查是否安全,所以在这种情况下要特别小心。
一个方法是否声明为同步取决于临界区访问,如果方法不访问临界区(共享资源或者数据结构)就没必要声明为同步的。
用户线程和守护线程有什么区别?
所谓守护线程,是指在程序运行的时候在后台提供一种通用服务的线程,比如垃圾回收线程就是一个很称职的守护者,并且这种线程并不属于程序中不可或缺的部分。因此,当所有的非守护线程结束时,程序也就终止了,同时会杀死进程中的所有守护线程。反过来说,只要任何非守护线程还在运行,程序就不会终止。
用户线程和守护线程两者几乎没有区别,唯一的不同之处就在于虚拟机的离开,如果用户线程已经全部退出运行了,只剩下守护线程存在了,虚拟机也就退出了。因为没有了被守护者,守护线程也就没有工作可做了,也就没有继续运行程序的必要了。
Java创建线程之后,直接调用start()方法和run()的区别:
1.run()方法,在本线程内调用该Runnable对象的run()方法,可以重复多次调用。
2.start()方法,启动一个线程,调用该Runnable对象的run()方法,不能多次启动一个线程。
当你调用start()方法时你将创建新的线程,并且执行在run()方法里的代码。但是如果你直接调用run()方法,它不会创建新的线程也不会执行调用线程的代码。
例子1:
/** *创建一个Bank银行类,属性有money(初始值为100),生成set/get方法,创建一个Bank银行对象, *创建三个线程(分别给线程命名为"用户A","用户B","用户C"), *当用户A线程执行时,通过set方法将money的值增加100,在控制台输出 "用户A线程正在执行第X次,增加了100元,目前money的值为X元" *当用户B线程执行时,通过set方法将money值随机增加1-100(不含100),在控制台输出"用户B线程正在执行第X次,增加了X元,目前money的值为X元" *当用户C线程执行时,线程休眠10毫秒,不作任何操作.在控制台输出"用户C线程正在执行第X次,睡眠了10毫秒" *共执行20次,最后打印输出money的值,如 "增加后的money值为:X元" *注意:(机子速度太快有可能会出现一条线程全执行完,关键要实现需求) */ public class Test01 { public static void main(String[] args) { BankThread b1 = new BankThread("用户A"); BankThread b2 = new BankThread("用户B"); BankThread b3 = new BankThread("用户C"); b2.start(); b3.start(); b1.start(); } } class BankThread extends Thread { BankThread(String str) { super(str); } static Bank b = new Bank(); static int count = 20; public void run() { while (true) { synchronized (BankThread.class) { if (count > 20) { break; } if ("用户A".equals(this.getName())) { b.setMoney(b.getMoney() + 100); System.out.println("用户A线程正在执行第" + count + "次,增加了100元,目前money的值为" + b.getMoney() + "元"); } else if ("用户B".equals(this.getName())) { Random r = new Random(); int num = r.nextInt(100); b.setMoney(b.getMoney() + num); System.out.println("用户B线程正在执行第" + count + "次,增加了" + num + "元,目前money的值为" + b.getMoney() + "元"); } else if ("用户C".equals(this.getName())) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("用户C线程正在执行第X次,睡眠了10毫秒"); } count++; } } }; } class Bank { private int money = 100; public int getMoney() { return money; } public void setMoney(int money) { this.money = money; } }
例子2:
/** * 模拟3个老师同时发80份笔记, 每个老师相当于一个线程, 分别给三个线程命名为”张老师线程,林老师线程,李老师线程”, * 要求在控制台输出"xxx老师"在发第"xxx"份笔记 2) 如果要求最后一张试卷必须由”李老师线程”发出,请问应该怎么做? */ public class Test02 { public static void main(String[] args) { ShiJuan shi = new ShiJuan(); Thread t1 = new Thread(shi); Thread t2 = new Thread(shi); Thread t3 = new Thread(shi); t1.setName("张老师"); t2.setName("林老师"); t3.setName("李老师"); t1.start(); t2.start(); t3.start(); } } class ShiJuan implements Runnable { int shijuan = 80; @Override public void run() { while (true) { synchronized (ShiJuan.class) { if (shijuan <= 0) { break; } try { Thread.sleep(30); } catch (InterruptedException e) { e.printStackTrace(); } if (shijuan == 1 && !Thread.currentThread().getName().equals("李老师")) { continue; } System.out.println(Thread.currentThread().getName() + "正在发" + shijuan); System.out.println(shijuan--); } } } }
例子3:
/* 模拟抢红包过程,生成50个红包(金额是随机的,范围在1-10元之间) 创建5个线程代表5个人,然后让这5个人去抢这50个红包,每次抢红包需要300ms的时间, 在控制台打印出(xxx抢了xxx元)(不限定每人抢的次数并且抢到红包后还可以接着抢,每次生成一个红包). */ public class Test03 { public static void main(String[] args) { HongBao_1 nb = new HongBao_1(); Thread t1 = new Thread(nb); Thread t2 = new Thread(nb); Thread t3 = new Thread(nb); Thread t4 = new Thread(nb); Thread t5 = new Thread(nb); t1.setName("线程1"); t2.setName("线程2"); t3.setName("线程3"); t4.setName("线程4"); t5.setName("线程5"); t1.start(); t2.start(); t3.start(); t4.start(); t5.start(); }} class HongBao_1 implements Runnable{ //这里的变量不需要静态 int hongbao=50; Random r = new Random(); @Override public void run() { while(true){ synchronized(HongBao_1.class){ if(hongbao<=0){ break; } try { Thread.sleep(30); } catch (InterruptedException e) { e.printStackTrace(); } int num = r.nextInt(10)+1; System.out.println(Thread.currentThread().getName()+"抢到了"+num+"元"); System.out.println(hongbao--); }} } }
例子4:
/** * 三个老师,发80份卷子,要求每个老师轮流发. */ public class Test04 { public static void main(String[] args) { final Paper p = new Paper(); new Thread("张老师") { public void run() { try { p.send1(); } catch (InterruptedException e) { e.printStackTrace(); } }; }.start(); new Thread("龚老师") { public void run() { try { p.send2(); } catch (InterruptedException e) { e.printStackTrace(); } }; }.start(); new Thread("刘老师") { public void run() { try { p.send3(); } catch (InterruptedException e) { e.printStackTrace(); } }; }.start(); } } class Paper { private int paper = 80; private int flag = 1; public void send1() throws InterruptedException { while (true) { synchronized (this) { while (flag != 1) { this.wait(); } if (paper == 0) { flag = 2; this.notifyAll(); break; } System.out.println(Thread.currentThread().getName() + "发了第" + paper-- + "份卷子"); flag = 2; this.notifyAll(); } } } public void send2() throws InterruptedException { while (true) { synchronized (this) { while (flag != 2) { this.wait(); } if (paper == 0) { flag = 3; this.notifyAll(); break; } System.out.println(Thread.currentThread().getName() + "发了第" + paper-- + "份卷子"); flag = 3; this.notifyAll(); } } } public void send3() throws InterruptedException { while (true) { synchronized (this) { while (flag != 3) { this.wait(); } if (paper == 0) { flag = 1; this.notifyAll(); break; } System.out.println(Thread.currentThread().getName() + "发了第" + paper-- + "份卷子"); flag = 1; this.notifyAll(); } } } }
例子5:
/**总和 = 从1加到10的和 + 从11加到20的和 *编写10个线程, 第一个线程从1加到10, 第二个线程从11加到20, ..., *第十个线程从91加到100, 最后再把10个线程的结果相加输出到控制台上 */ public class Test05 { public static void main(String[] args) throws InterruptedException { for (int i = 0; i < 10; i++) { XiangJia xj = new XiangJia(); xj.setName("线程" + (i + 1)); xj.setInit(i * 10); xj.start(); } Thread.sleep(10); } } class XiangJia extends Thread { static int total; int inti; public void setInit(int inti) { this.inti = inti; } @Override public void run() { synchronized (XiangJia.class) { int sum = 0; for (int i = 1; i <= 10; i++) { sum += inti + i; } total += sum; System.out.println(getName() + "...total=" + total); } } }
例子6:
/** * 某包子店铺生意火爆,现开5个窗口模拟售卖100个包子,每次每个窗口随机卖出1-5个包子, 卖完最后一个包子后提示”包子已售完”(必须全部卖出),程序结束. */ public class Test06 { public static void main(String[] args) { Baozi bz1 = new Baozi("窗口1"); Baozi bz2 = new Baozi("窗口2"); Baozi bz3 = new Baozi("窗口3"); Baozi bz4 = new Baozi("窗口4"); Baozi bz5 = new Baozi("窗口5"); bz1.start(); bz2.start(); bz3.start(); bz4.start(); bz5.start(); } } class Baozi extends Thread { Baozi(String str) { super(str); } // 包子总数 static int baozi = 100; Random r = new Random(); @Override public void run() { while (true) { synchronized (Baozi.class) { if (baozi <= 0) { break; } try { Thread.sleep(30); } catch (InterruptedException e) { e.printStackTrace(); } int nextInt = r.nextInt(5) + 1; if (baozi <= nextInt) {// 此时包子已经卖到不足5个 nextInt = baozi; System.out.println(this.getName() + "卖了" + nextInt + "个"); System.out.println("包子已经卖完!!!!"); } else { // 此时包子是大于5个的 System.out.println(this.getName() + "卖了" + nextInt + "个"); } baozi -= nextInt; System.out.println(baozi); } } } }