本文章的所有代码
零碎知识
任何一个线程都具有基本的组成部分:
① CPU时间片:操作系统 (OS)为每个线程分配的执行时间
② 运行数据:
–堆空间:存储线程需要使用的对象,多个线程可以共享堆中的对象
–栈空间:存储线程需要使用的局部变量,每个线程都拥有独立的栈,不共享
③ 线程的具体代码
①. 线程抢占式执行:
–效率高
–可防止单一线程长时间独占CPU
②. 在 单核 CPU中:宏观并行,微观串行
Thread类的中文文档
Runnable中文文档
继承Thread类的创建方式:
换句话说就是继承Thread,重写run() 方法,就可以使用了
实现Runnable接口的创建方式:
可以简写成:(没必要包装成Runnable的)
实现Runnable接口的还有一种写法——匿名内部类
简写:
抢占式执行演示:
获取线程的ID和Name:(推荐Thread.currentThread() 方式)
设置线程Name:(ID无法修改)
★ 用run()启动和用start()启动的区别: start()并行,run()串行
用run() 启动的线程类失去了多线程的意义,跟普通类没区别
(有种说法:run() 是在调用线程对象的方法,就跟调用普通对象的方法一样,但run() 确实也启动了线程类,可以看下下面两张图的区别)
初始态:只在堆中开辟内存与普通对象无异
就绪态:执行了start()方法,等待操作系统分配CPU时间片给它
运行态:操作系统给予CPU时间片,正在执行
等待态:线程进入等待或休眠状态,放弃CPU片,但不一定会释放锁和其他资源
阻塞:阻塞状态是指线程因为某种原因失去了cpu使用权,暂时停止运行,直到线程重新进入运行态。
终止态:线程结束生命周期
jdk1.5后 就绪态和运行态合并为 运行态Runnable
状态图:
Thread源码中的所有状态值:
TIMED_WAITING即sleep,指定时间长度的等待
⑵yield() 放弃:主动放弃CPU一次,重新等待操作系统分配CPU时间片。
public class ThreadMethods {
public static void main(String[] args) {
new Thread(new YieldThread()).start();
new Thread(new PojoThread()).start();
}
}
class YieldThread implements Runnable{
public void run() {
int i =0 ;
while(i<20) {
++i;
yy();
System.out.println("Yield:我这是第"+(++i)+"次运行,休息一秒");
}
}
void yy() {
System.out.println("Yield:我放弃了,这次不打印,没心情");
Thread.yield();
}
}
class PojoThread implements Runnable{
public void run() {
int i =0 ;
while(i<20) {
System.out.println("Pojo:我这是第"+(++i)+"次运行,休息一秒");
}
}
}
⑶join() 线程加入:新线程(join)加入当前线程,阻塞当前线程,等执行完新线程(join)后再释放执行当前线程。
换句话就是插队,办完事再还当前线程的CPU
public class ThreadMethods {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new JoinThread());
t1.start();
t1.join();//插队,抢当前线程CPU(main线程)
for (int j = 1; j < 10; j++) {
System.out.println("main:我这是第"+(j)+"次运行");
}
}
}
class JoinThread implements Runnable{
@Override
public void run() {
int i =0 ;
System.out.println("Join:插个队,等我全部打印完你再执行。");
while(i<5) {
System.out.println("Join:我这是第"+(++i)+"次运行-----------");
}
System.out.println("Join:我全部打印完了,还你CPU。");
}
}
只会阻塞当前线程(在哪个线程里写的join,就是哪个),不会阻塞同时运行的其他线程
public class ThreadMethods {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new JoinThread());
t1.start();
new Thread(new PojoThread()).start();
//只会阻塞当前线程(main线程),不会阻塞同时运行的其他线程
t1.join();//插队,抢当前线程CPU(main线程)
for (int j = 1; j < 10; j++) {
System.out.println("main:我这是第"+(j)+"次运行");
}
}
}
class JoinThread implements Runnable{
@Override
public void run() {
int i =0 ;
System.out.println("Join:插个队,等我全部打印完你再执行。");
while(i<10) {
System.out.println("Join:我这是第"+(++i)+"次运行-----------");
}
System.out.println("Join:我全部打印完了,还你CPU。");
}
}
class PojoThread implements Runnable{
public void run() {
int i =0 ;
while(i<10) {
System.out.println("Pojo:我就是不让join,不能惯他,这是第"+(++i)+"次运行");
}
}
}
Join:插个队,等我全部打印完你再执行。
Pojo:我就是不让join,不能惯他,这是第1次运行
Join:我这是第1次运行-----------
Pojo:我就是不让join,不能惯他,这是第2次运行
Join:我这是第2次运行-----------
Pojo:我就是不让join,不能惯他,这是第3次运行
Join:我这是第3次运行-----------
Pojo:我就是不让join,不能惯他,这是第4次运行
Join:我这是第4次运行-----------
Pojo:我就是不让join,不能惯他,这是第5次运行
Join:我这是第5次运行-----------
Pojo:我就是不让join,不能惯他,这是第6次运行
Join:我这是第6次运行-----------
Pojo:我就是不让join,不能惯他,这是第7次运行
Join:我这是第7次运行-----------
Pojo:我就是不让join,不能惯他,这是第8次运行
Join:我这是第8次运行-----------
Pojo:我就是不让join,不能惯他,这是第9次运行
Join:我这是第9次运行-----------
Pojo:我就是不让join,不能惯他,这是第10次运行
Join:我这是第10次运行-----------
Join:我全部打印完了,还你CPU。
main:我这是第1次运行
main:我这是第2次运行
main:我这是第3次运行
main:我这是第4次运行
main:我这是第5次运行
main:我这是第6次运行
main:我这是第7次运行
main:我这是第8次运行
main:我这是第9次运行
⑷设置线程优先级:1~10,1最低,10最高,默认5,越高越容易抢到CPU时间片
public class ThreadMethods {
public static void main(String[] args) throws InterruptedException {
new Thread(new PriorityThread(1),"p1").start() ;
new Thread(new PriorityThread(5),"p5").start() ;
new Thread(new PriorityThread(10),"p10").start();
}
}
class PriorityThread extends Thread{
public PriorityThread() {
super();
}
public PriorityThread(int p) {
super();
//这里不能this.setPriority(p),这样不会设置成功的
Thread.currentThread().setPriority(p);
}
@Override
public void run() {
for (int i = 0; i < 50; i++) {
System.out.print (Thread.currentThread().getName()+" Priority: " + i);
}
System.out.println("\r\n"+Thread.currentThread().getPriority()+ " " +Thread.currentThread().getName()+" Priority: 我打印完了---------------------------");
}
}
⑸守护线程(后台线程):setDaemon(true),默认false(前台)
public class ThreadMethods {
public static void main(String[] args) throws InterruptedException {
new Thread(new PojoThread("普通线程")).start() ;
Thread t2 = new DaemonThread();
t2.setDaemon(true);//注销则是前台线程,不会自动停止
t2.start();
}
}
class DaemonThread extends Thread{
public DaemonThread() {
super();
}
public void run() {
while(true)
System.out.println("Daemon:我是守护线程,即后台线程 ,等所有前台线程结束后自动结束 "
+Thread.currentThread().isDaemon());
}
}
用同步代码块解决(这里先简单的讲一下,实际应用不这样)
import java.util.Arrays;
public class ThreadMethods {
static int index = 0;
public static void main(String[] args) throws InterruptedException {
Object socket = new Object();
String[] str = new String[10];
Runnable r1 = new Runnable() {
public void run() {
// synchronized (socket) {
str[index++] = "hello";
str[index++] = "hello";
// }
}; };
Runnable r2 = new Runnable() {
public void run() {
// synchronized (socket) {
str[index++] = "world";//访问并修改共享资源
str[index++] = "world";//访问并修改共享资源
// }
}; };
Thread t1 = new Thread(r1,"r1");
Thread t2 = new Thread(r2,"r2");
t1.start(); t2.start();
t1.join(); t2.join();
System.out.println(Arrays.toString(str));
}
}
加同步锁就不会出现(一般情况没有,实际上还是有问题的)
卖票例子:
import java.util.Arrays;
public class ThreadMethods {
static int index = 0;
public static void main(String[] args) throws InterruptedException {
TicketRunnable tickets = new TicketRunnable();
Thread t1 = new Thread(tickets,"t1");
Thread t2 = new Thread(tickets,"t2");
Thread t3 = new Thread(tickets,"t3");
Thread t4 = new Thread(tickets,"t4");
t1.start(); t2.start();t3.start(); t4.start();
}
}
class TicketRunnable implements Runnable{
private int ticket = 200;
@Override
public void run() {
while(true) {
if(ticket <= 0)//访问共享资源
break;
System.out.println(Thread.currentThread().getName()
+ "卖了第"+ticket +"张票");//访问共享资源
ticket--;//修改共享资源
}
}
}
public class ThreadMethods {
static int index = 0;
public static void main(String[] args) throws InterruptedException {
TicketRunnable tickets = new TicketRunnable();
Thread t1 = new Thread(tickets,"t1");
Thread t2 = new Thread(tickets,"t2");
Thread t3 = new Thread(tickets,"t3");
Thread t4 = new Thread(tickets,"t4");
t1.start(); t2.start();t3.start(); t4.start();
}
}
class TicketRunnable implements Runnable{
private int ticket = 200;
private Object ticketSocket= new Object();
@Override
public void run() {
while(true) {
synchronized (ticketSocket) {
//ticketSocket是该同步代码块的锁,保证锁唯一就行,Object对象
if(ticket <= 0)//访问共享资源
break;
System.out.println(Thread.currentThread().getName()
+ "卖了第"+ticket +"张票");//访问共享资源
ticket--;//修改共享资源
} } }
}
错误加锁的例子之一:位置不对
当然,不是说加同步锁的范围越大越好,要考虑运行效率和其他的问题
★ 能不加锁尽量不加锁
★ 同步方法的锁只有两种:
public class ThreadMethods {
static int index = 0;
public static void main(String[] args) throws InterruptedException {
SynchronizedMethod synMethod = new SynchronizedMethod();
Thread t1 = new Thread(synMethod,"t1");
Thread t2 = new Thread(synMethod,"t2");
Thread t3 = new Thread(synMethod,"t3");
Thread t4 = new Thread(synMethod,"t4");
t1.start(); t2.start();t3.start(); t4.start();
}
}
class SynchronizedMethod implements Runnable{
private int ticket = 200;
private Object ticketSocket= new Object();
@Override
public void run() { synMethod(); }
//同步方法
public synchronized void synMethod() {
//锁是this,this是执行同步方法的对象——>main()里的TicketRunnable tickets
while(true) {
if(ticket <= 0)//访问共享资源
break;
System.out.println(Thread.currentThread().getName()
+ "卖了第"+ticket +"张票");//访问共享资源
ticket--;//修改共享资源
}
}
//如果写成静态同步方法,如
//public static synchronized void synMethod(){}
//那么锁是类本身,————>SynchronizedMethod.class
}
JDK中线程安全的一些常见类:
死锁:A、B两线程互相持有双方所需要的锁,却互不相让,就形成死锁
男孩和女孩一起吃饭的例子:(只有一双筷子)
public class ThreadMethods {
static int index = 0;
public static void main(String[] args) throws InterruptedException {
new Thread(new BoyRunnable()).start();
new Thread(new GirlRunnable()).start();
}
}
class MyLock {
static Object a = "a";
static Object b = "b";
}
class BoyRunnable implements Runnable{
public void run() {
synchronized (MyLock.a) {
System.out.println("男孩拿到a筷子");
synchronized (MyLock.b) {
System.out.println("男孩拿到了b筷子");
System.out.println("男孩可以吃饭了");
}
}
}
}
class GirlRunnable implements Runnable{
public void run() {
synchronized (MyLock.b) {
System.out.println("女孩拿到b筷子");
synchronized (MyLock.a) {
System.out.println("女孩拿到了a筷子");
System.out.println("女孩可以吃饭了");
}
}
}
}
互不想让,形成死锁
形成死锁的线程不会释放已经拥有的锁,这样会一直堵塞,例如:
OtherBoyRunnable 永远也拿不到 BoysRunnable 手中的a锁
public class ThreadMethods {
static int index = 0;
public static void main(String[] args) throws InterruptedException {
new Thread(new BoysRunnable()).start();
new Thread(new GirlRunnable()).start();
Thread.sleep(100);
new Thread(new OtherBoyRunnable()).start();
}
}
class MyLock {
static Object a = "a"; static Object b = "b";
static Object c = "c"; static Object d = "d";
}
class GirlRunnable implements Runnable{
public void run() {
synchronized (MyLock.b) {
System.out.println("女孩拿到b锁,想继续拿a锁");
synchronized (MyLock.a) {
System.out.println("女孩拿到了a锁");
}
}
}
}
class BoysRunnable implements Runnable{//多情boys
public void run() {
synchronized (MyLock.a) {
System.out.println("boys:拿到a锁,想继续拿b锁");
synchronized(MyLock.b) {
System.out.println("boys:拿到a锁和b锁");
synchronized(MyLock.c) {
System.out.println("boys:拿到a锁、b锁和c锁");
}
}
}
}
}
class OtherBoyRunnable implements Runnable{
public void run() {
System.out.println("OtherBoy:我什么锁都没拿到,我想要拿到a锁");
synchronized (MyLock.a) {
System.out.println("OtherBoy:我拿到a锁了。。。。。");
}
}
}
存取钱的例子:
public class ThreadMethods {
static int index = 0;
public static void main(String[] args) throws InterruptedException {
BankCard card = new BankCard();
Thread t1 = new Thread(new AddMoneyRunnable(card),"男一号");
Thread t3 = new Thread(new SubMoneyRunnable(card),"女一号");
t1.start();
t3.start();
}
}
class BankCard {//银行卡类
private double money = 0;
private boolean flag = false;
//true:有钱,能取,不能存
//false:没钱,不能取,能存
public synchronized void addMoney() throws InterruptedException {
if(flag) // this——>main()中的 BankCard card 对象,保证了锁的唯一型
this.wait();//有钱,不能再存了
money += 1000; // 存钱
System.out.println(Thread.currentThread().getName()+"存了1000, 余额:"+money);
flag = true; // 修改标记,可以取钱了,不能再存钱
this.notify(); //随机唤醒等待队列里的 一个 线程
}
public synchronized void subMoney() throws InterruptedException {
if(!flag) // flag = false 时进入等待队列
this.wait();//没钱,不能再取了
money -= 1000; // 取钱
System.out.println(Thread.currentThread().getName()+"取了1000, 余额:"+money);
flag = false; // 修改标记,可以存钱了,不能再取钱
this.notify(); //随机唤醒等待队列里的 一个 线程
}
}
class SubMoneyRunnable implements Runnable{
private BankCard card;
public SubMoneyRunnable(BankCard card) {
this.card = card;
}
public void run() {
while(true)
try {
card.subMoney();
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
class AddMoneyRunnable implements Runnable{
private BankCard card;
public AddMoneyRunnable(BankCard card) {
this.card = card;
}
public void run() {
while(true) {
try {
card.addMoney();
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
上面只有一存一取,两个人时,就没问题,当多人存多人取,就会出问题:
★ 问题1:多存多取——>突破上下限
只修改main() 方法,加多两个人,还是只有一张银行卡
好家伙,钱越来越多( 要是真能如此,就不用打工了)
还有一种越来越少的:
这就是多存多取的问题,问题的根源在这里:
★ 通过wait() 进入等待的线程 在被重新唤醒后 会从wait语句后接着执行
★ 上面的线程被唤醒后不会重新判断flag,没更新flag状态
解决方法: 让被唤醒的线程重新判断flag就行,将两个if改成while就行
造成上面越存越多的问题的过程:
总结:多存多取,突破上下限的原因——使用if判断的线程在被唤醒后,不会重新判断条件是否符合
★ 问题2:唤醒后重复判断——>死锁?
改完while,问题又来了,不打印了,卡住了
问题根源:一些线程腿太长,连续抢到CPU,自投罗网,加上随机唤醒一个的时候没唤醒对的人
★ 不是 死锁,是所有线程都在wait,都进入等待队列了(两个取钱的速度太快了,像极了我)
造成全员等待的过程:(可能性之一,这个符合上面的结果而已)
总结:造成全员等待的原因是——某个线程 连续 抢到线程后,随机唤醒时又错误唤醒了其他线程
⑽生产者与消费者例子:
public class ThreadMethods {
static int index = 0;
public static void main(String[] args) throws InterruptedException {
BreadCon breadCon = new BreadCon();
Thread p1 = new Thread(new ProducerRunnable(breadCon), "生产者1号");
Thread c1 = new Thread(new ConsumerRunnable(breadCon), "消费者1号");
Thread p2 = new Thread(new ProducerRunnable(breadCon), "生产者2号");
Thread c2 = new Thread(new ConsumerRunnable(breadCon), "消费者2号");
p1.start(); c1.start();
p2.start(); c2.start();
}
}
class BreadProduct {
private int id ;
private String ProductName;
public BreadProduct() {
super();
}
public BreadProduct(String productName) {
super();
this.ProductName = productName;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getProductName() {
return ProductName;
}
public void setProductName(String productName) {
ProductName = productName;
}
@Override
public String toString() {
return "BreadProduct [id=" + id + ", ProductName=" + ProductName + "]";
}
}
class BreadCon { //仓库
private BreadProduct[] breads = new BreadProduct[20];
private int index = 0;
private int breadId = 0;
public synchronized void input(BreadProduct bread) throws InterruptedException {
while(index > (breads.length-1))
this.wait();
bread.setId(++breadId);
breads[index++] = bread;
System.out.println(Thread.currentThread().getName() + "生产了"+bread.getId()+"号面包");
this.notifyAll();
}
public synchronized void output() throws InterruptedException {
while(index < 1)
this.wait();
--index;
System.out.println(Thread.currentThread().getName() + "消费了"
+ breads[index].getId() + "号面包,该面包生产者:"
+ breads[index].getProductName());
breads[index] = null;
this.notifyAll();
}
}
class ProducerRunnable implements Runnable{
private BreadCon breadCon;
public ProducerRunnable(BreadCon breadCon) {
super();
this.breadCon = breadCon;
}
public void run() {
try {
for (int i = 0; i < 30; i++)
breadCon.input(new BreadProduct(Thread.currentThread().getName()));
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
class ConsumerRunnable implements Runnable{
private BreadCon breadCon;
public ConsumerRunnable(BreadCon breadCon) {
super();
this.breadCon = breadCon;
}
public void run() {
try {
for (int i = 0; i < 30; i++)
breadCon.output();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
回到目录
本文章的所有代码:密码1234
链接:https://pan.baidu.com/s/1sV7KN-ZoCYeN_FT2VZS6NA
提取码:1234
零碎知识:
线程执行了yield()后,并不会释放锁的例子
public class ThreadMethods {
static int index = 0;
public static void main(String[] args) throws InterruptedException {
new Thread(new YieldRunnable2(), "yield").start();
Thread.sleep(1000);//确保a锁先被YieldRunnable2拿到
new Thread(new OtherBoyRunnable(), "other").start();
}
}
class YieldRunnable2 implements Runnable{
public void run() {
int number = 2;
synchronized (MyLock.a) {
System.out.println("我拿到了a锁");
for (int i = 0; i < 10; i++) {
System.out.println("yield:我让步了,放弃了CPU,但是还拿着a锁");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Thread.yield();
}
System.out.println("yield:我运行完了,释放a锁");
}
}
}
class OtherBoyRunnable implements Runnable{
public void run() {
System.out.println("OtherBoy:我什么锁都没拿到,我想要拿到a锁");
synchronized (MyLock.a) {
System.out.println("OtherBoy:我拿到a锁了。。。。。");
}
}
}