线程:
是独立执行的路径,程序运行时即使没有自己创建线程,后台也会有线程,如主线程main()为系统的入口,用于执行整个程序
当我们运行一个程序时,在main函数中调用其他方法时只能跑完调用的这个方法才能继续执行其他方法。
多线程:主线程和子线程同时进行,同时执行多个方法
线程的创建:
1、继承Thread类
2、实现Runnable接口
3、实现Callable接口(了解不做赘述)
继承Thread类:
首先继承Thread类,后重写run()方法,再调用start()方法启动线程
package com.thread;
public class ThreadDemo1 extends Thread {
public static void main(String[] args) {
ThreadDemo1 td=new ThreadDemo1();//创建线程对象
td.start(); //调用start方法启动线程
for( int i=0;i<20;i++) {
System.out.println("study Thread");
}
}
public void run() { //重写run方法
for (int i=0;i<200;i++) {
System.out.println("多线程学习");
}
}
}
实现Runnable接口法:
先实现Runnable,重写run方法,调用start启动程序,此种方法接口的实现类对象将由线程对象开启:
new Thread(接口的实现类对象).start();
演示代码:
package com.thread;
public class RunnableDemo implements Runnable {
public static void main(String[] args) {
RunnableDemo td=new RunnableDemo();
new Thread(td).start();
for( int i=0;i<200;i++) {
System.out.println("study Thread");
}
}
public void run() {
for (int i=0;i<200;i++) {
System.out.println("多线程学习");
}
}
}
操作以上两种方法都具有多线程的能力,但继承Thread方法只能单继承有一定的局限性(Java只能单继承)而实现Runnable接口可以避免单继承的局限性(接口可以多实现)资源共享比较容易
静态代理:
线程状态:
线程停止:
方法一:stop方法,代码逻辑没有运行完,就退出
方法二:通过标识位,停止线程
方法三:通过标识位+异常处理结束线程
线程休眠:
线程休眠是帮助所有线程获得运行机会的最好方法
当调用sleep()方法时(sleep方法形参为毫秒数)线程进入休眠状态,不再继续执行,休眠时间结束后线程进入准备状态,等待执行。
package com.thread;
public class ThreadSleep {
public static void main(String[] args) {
Sleep();
}
public static void Sleep() {
for(int i=0;i<=10;i++) {
try {
Thread.sleep(1000);//线程休眠时间为一秒
System.out.println(i);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
输出结果为: 0 ,1,2····10。由于线程休眠时间设置为一秒,所以每隔一秒钟输出一个数字。
线程礼让:
Thread.yield();
暂停当前线程的运行状态使其进入准备状态,重新执行(可能在其他线程之前,也可能在后面)
线程强制执行
jion()方法,只有等待此线程执行完之后,其他线程才能执行
package com.thread;
public class Threadijion implements Runnable{
public static void main(String[] args) {
Threadijion th=new Threadijion();
Thread t=new Thread(th);
t.start();
try {
for(int i=0;i<100;i++) {
if(i==50) {
t.join(); //当i==50的时候此线程中止执行,开始执行run
}
System.out.println("我先执行"+i);
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void run() {
// TODO Auto-generated method stub
for(int i=0;i<100;i++)
System.out.println("线程合并"+i);
}
当主线程里i==50时此线程中止执行,开始执行下面run,当join的线程执行完毕后主线程继续执行。
小结
1、调用Thread.sleep():使当前线程睡眠至少多少毫秒(它可能在指定的时间之前被中断)。
2、调用Thread.yield():有机会使其他线程先于此线程执行
3、调用join()方法:当前线程停止执行,直到该线程所加入的线程完成为止。然而,如果它加入的线程没有存活,则当前线程不需要停止。
观测线程状态
NEW:尚未启动的线程
RUNNABLE:在JAVA虚拟机中执行的线程
BLOCKED:被阻塞等待监视器锁定的线程
WAITING:正在等待另一个线程执行特定动作的线程
TIMED_ WAITING:正在等在另一个线程
TERNINATED:已退出的线程
package com.thread;
public class RunnableDemo implements Runnable {
public static void main(String[] args) {
RunnableDemo td=new RunnableDemo();
Thread T=new Thread(td);
Thread.State state=T.getState();
System.out.println(state);
T.start();
state=T.getState();
System.out.println(state);
for( int i=0;i<1;i++) {
System.out.println("study Thread");
}
}
public void run() {
for (int i=0;i<1;i++) {
System.out.println("多线程学习");
}
}
}
输出结果:
NEW
RUNNABLE
线程优先级
1.线程的优先级范围从1-10
Thread.MIN_PRIORITY = 1;
Thread.MAX_PRIORITY = 10;
Thread.NORM_PRORRITY = 5;
2.优先级的设定建议在start()调用前
3.方法
获取优先级 :gePriority()
设置优先级 :setPriority()
注意:优先级低只是意味着获得调度的概率低,并不是不会被调用。
public class PriorityTest {
public static void main(String[] args) {
MyPriority myPriority = new MyPriority();
Thread t1 = new Thread(myPriority);
Thread t2 = new Thread(myPriority);
Thread t3 = new Thread(myPriority);
Thread t4 = new Thread(myPriority);
System.out.println(Thread.currentThread().getName() + "的优先级:" + Thread.currentThread().getPriority());
t1.setPriority(1);
t1.start();
t2.setPriority(10);
t2.start();
t3.setPriority(5);
t3.start();
t4.setPriority(8);
t4.start();
}
}
class MyPriority implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "的优先级:" + Thread.currentThread().getPriority());
}
}
结果:
运行结果一: main的优先级——>5
Thread-0的优先级——>1
Thread-1的优先级——>10
Thread-4的优先级——>3
Thread-3的优先级——>8
Thread-2的优先级——>5
运行结果二: main的优先级——>5
Thread-1的优先级——>10
Thread-3的优先级——>8
Thread-0的优先级——>1
Thread-2的优先级——>5
Thread-4的优先级——>3
守护(daemon)线程
1.线程分为用户线程和守护线程
2.虚拟机必须确保用户线程执行完毕,不用等待守护线程执行完毕
设置是否为守护线程,默认是false
void setDaemon(boolean on)
public class DaemonTest {
public static void main(String[] args) {
You you = new You();
God god = new God();
Thread t1 = new Thread(god);
t1.setDaemon(true);
t1.start();
new Thread(you).start();
}
}
class God implements Runnable{
@Override
public void run() {
while (true){
System.out.println("first");
}
}
}
class You implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(i);
}
System.out.println("end");
}
}
线程同步
1、同步代码块
并发:同一个对象被多个线程同时操作
1.同步方法仍然设计到同步监视器,只是不需要我们显式的声明。
2.非静态的同步方法,同步监视器是:this
静态的同步方法,同步监视器是:当前类本身
publicclass Test {
publicstaticvoid main(String[] args) {
User u = new User("张三", 100);
MyThread t1 = new MyThread("线程A", u, 20);
MyThread t2 = new MyThread("线程B", u, -60);
MyThread t3 = new MyThread("线程C", u, -80);
MyThread t4 = new MyThread("线程D", u, -30);
MyThread t5 = new MyThread("线程E", u, 32);
MyThread t6 = new MyThread("线程F", u, 21);
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
t6.start();
}
}
class MyThread extends Thread {
private User u;
privateint y = 0;
MyThread(String name, User u, int y) {
super(name);
this.u = u;
this.y = y;
}
publicvoid run() {
u.oper(y);
}
}
class User {
private String code;
privateint cash;
User(String code, int cash) {
this.code = code;
this.cash = cash;
}
public String getCode() {
return code;
}
publicvoid setCode(String code) {
this.code = code;
}
publicsynchronizedvoid oper(int x) {
try {
Thread.sleep(10L);
this.cash += x;
System.out.println(Thread.currentThread().getName() + "运行结束,增加“" + x +"”,当前用户账户余额为:" + cash);
Thread.sleep(10L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public String toString() {
return"User{" +
"code='" + code + '\'' +
", cash=" + cash +
'}';
}
}
输出结果:
输出结果:
线程A运行结束,增加“20”,当前用户账户余额为:120
线程F运行结束,增加“21”,当前用户账户余额为:141
线程E运行结束,增加“32”,当前用户账户余额为:173
线程C运行结束,增加“-80”,当前用户账户余额为:93
线程B运行结束,增加“-60”,当前用户账户余额为:33
线程D运行结束,增加“-30”,当前用户账户余额为:3
2、同步方法
1.如何定义
synchronized(同步监视器){//需要被同步的代码}
2.操作共享数据的代码,即为需要被同步的代码。
3.共享数据:多个线程共同操作的变量
4.同步监视器,俗称锁。任何一个类的对象,都可以充当锁。要求:多个线程必须要共用同一把锁。
5.实现Runnable接口创建多线程的方式中,我们可以考虑使用this充当同步监视器
6.在继承Thread类创建多线程的方式中,考虑使用当前类充当同步监视器。慎用this充当同步监视器。
publicclass Test {
publicstaticvoid main(String[] args) {
User u = new User("张三", 100);
MyThread t1 = new MyThread("线程A", u, 20);
MyThread t2 = new MyThread("线程B", u, -60);
MyThread t3 = new MyThread("线程C", u, -80);
MyThread t4 = new MyThread("线程D", u, -30);
MyThread t5 = new MyThread("线程E", u, 32);
MyThread t6 = new MyThread("线程F", u, 21);
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
t6.start();
}
}
class MyThread extends Thread {
private User u;
privateint y = 0;
MyThread(String name, User u, int y) {
super(name);
this.u = u;
this.y = y;
}
publicvoid run() {
u.oper(y);
}
}
class User {
private String code;
privateint cash;
User(String code, int cash) {
this.code = code;
this.cash = cash;
}
public String getCode() {
return code;
}
publicvoid setCode(String code) {
this.code = code;
}
/**
* 业务方法
*
* @param x 添加x万元
*/
publicvoid oper(int x) {
try {
Thread.sleep(10L);
synchronized (this) {
this.cash += x;
System.out.println(Thread.currentThread().getName() +"运行结束,增加“" + x +"”,当前用户账户余额为:" + cash);
}
Thread.sleep(10L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public String toString() {
return"User{" +
"code='" + code + '\'' +
", cash=" + cash +
'}';
}
}
输出结果:
线程E运行结束,增加“32”,当前用户账户余额为:132
线程B运行结束,增加“-60”,当前用户账户余额为:72
线程D运行结束,增加“-30”,当前用户账户余额为:42
线程F运行结束,增加“21”,当前用户账户余额为:63
线程C运行结束,增加“-80”,当前用户账户余额为:-17
线程A运行结束,增加“20”,当前用户账户余额为:3
3、JUC安全类型的集合
public class JUCTest {
public static void main(String[] args) {
CopyOnWriteArrayList list = new CopyOnWriteArrayList();
for (int i = 0; i < 10000; i++) {
new Thread(()->{
list.add(Thread.currentThread().getName());
}).start();
}
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(list.size());
}
}
运行结果:10000
4、死锁问题
线程发生死锁可能性很小,即使看似可能发生死锁的代码,在运行时发生死锁的可能性也是小之又小。
publicclass Test {
publicstaticvoid main(String[] args) {
DeadlockRisk dead = new DeadlockRisk();
MyThread t1 = new MyThread(dead, 1, 2);
MyThread t2 = new MyThread(dead, 3, 4);
MyThread t3 = new MyThread(dead, 5, 6);
MyThread t4 = new MyThread(dead, 7, 8);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class MyThread extends Thread {
private DeadlockRisk dead;
privateint a, b;
MyThread(DeadlockRisk dead, int a,int b) {
this.dead = dead;
this.a = a;
this.b = b;
}
@Override
publicvoid run() {
dead.read();
dead.write(a, b);
}
}
class DeadlockRisk {
privatestaticclass Resource {
publicint value;
}
private Resource resourceA =new Resource();
private Resource resourceB =new Resource();
publicint read() {
synchronized (resourceA) {
System.out.println("read():" + Thread.currentThread().getName() +"获取了resourceA的锁!");
synchronized (resourceB) {
System.out.println("read():" + Thread.currentThread().getName() +"获取了resourceB的锁!");
return resourceB.value + resourceA.value;
}
}
}
publicvoid write(int a,int b) {
synchronized (resourceB) {
System.out.println("write():" + Thread.currentThread().getName() +"获取了resourceA的锁!");
synchronized (resourceA) {
System.out.println("write():" + Thread.currentThread().getName() +"获取了resourceB的锁!");
resourceA.value = a;
resourceB.value = b;
}
}
}
}
发生死锁的原因一般是两个对象的锁相互等待造成的。
5、Lock(锁)
1.创建ReentrantLock对象
final ReentrantLock lock = new ReentrantLock()
2.加锁
void lock()
3.解锁
void unlock()
public class LockTest {
public static void main(String[] args) {
Ticket ticket = new Ticket();
new Thread(ticket,"李四").start();
new Thread(ticket,"张三").start();
new Thread(ticket,"王五").start();
}
}
class Ticket implements Runnable{
private static int ticketNum = 10;
//定义Lock锁
private final ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
try {
//加锁
lock.lock();
while (true){
if(ticketNum > 0){
System.out.println(Thread.currentThread().getName() + "拿到了第" + ticketNum-- + "张票");
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
break;
}
}
}finally {
//解锁
lock.unlock();
}
}
}
6、Lock(锁)与synchronized的对比
1.Lock是显式锁(手动开启和关闭),synchronized是隐式锁,出了作用域自动释放
2.Lock只有代码块锁,synchronized有代码块锁和方法锁
3.使用Lock锁,性能更好,并且具有更好的扩展性(提供更多的子类)
4.优先使用顺序:
Lock > 同步代码块(已经进入了方法体,分配了相应资源) > 同步方法(在方法体之外)
五、线程通信问题
1、常用方法
1、线程一直等待,直到其他线程通知,与sleep不同,会释放锁。
void wait()
2、指定等待的毫秒数
void wait(long timeout)
3、唤醒一个处于等待状态的线程
void notify()
4、唤醒同一个对象上所有调用wait()方法的线程,优先级高的线程优先调度
void notifyAll()
2、生产者消费者模型
publicclass Test {
publicstaticvoid main(String[] args) {
Godown godown = new Godown(30);
Consumer c1 = new Consumer(50, godown);
Consumer c2 = new Consumer(20, godown);
Consumer c3 = new Consumer(30, godown);
Producer p1 = new Producer(10, godown);
Producer p2 = new Producer(10, godown);
Producer p3 = new Producer(10, godown);
Producer p4 = new Producer(10, godown);
Producer p5 = new Producer(10, godown);
Producer p6 = new Producer(10, godown);
Producer p7 = new Producer(80, godown);
c1.start();
c2.start();
c3.start();
p1.start();
p2.start();
p3.start();
p4.start();
p5.start();
p6.start();
p7.start();
}
}
/**
* 仓库
*/
class Godown {
publicstaticfinalint max_size = 100;//最大库存量
publicint curnum; //当前库存量
Godown() {
}
Godown(int curnum) {
this.curnum = curnum;
}
/**
* 生产指定数量的产品
*
* @param neednum
*/
publicsynchronizedvoid produce(int neednum) {
//测试是否需要生产
while (neednum + curnum > max_size) {
System.out.println("要生产的产品数量" + neednum +"超过剩余库存量" + (max_size - curnum) +",暂时不能执行生产任务!");
try {
//当前的生产线程等待
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//满足生产条件,则进行生产,这里简单的更改当前库存量
curnum += neednum;
System.out.println("已经生产了" + neednum +"个产品,现仓储量为" + curnum);
//唤醒在此对象监视器上等待的所有线程
notifyAll();
}
/**
* 消费指定数量的产品
*
* @param neednum
*/
publicsynchronizedvoid consume(int neednum) {
//测试是否可消费
while (curnum < neednum) {
try {
//当前的生产线程等待
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//满足消费条件,则进行消费,这里简单的更改当前库存量
curnum -= neednum;
System.out.println("已经消费了" + neednum +"个产品,现仓储量为" + curnum);
//唤醒在此对象监视器上等待的所有线程
notifyAll();
}
}
/**
* 生产者
*/
class Producer extends Thread {
privateint neednum; //生产产品的数量
private Godown godown; //仓库
Producer(int neednum, Godown godown) {
this.neednum = neednum;
this.godown = godown;
}
publicvoid run() {
//生产指定数量的产品
godown.produce(neednum);
}
}
/**
* 消费者
*/
class Consumer extends Thread {
privateint neednum; //生产产品的数量
private Godown godown; //仓库
Consumer(int neednum, Godown godown) {
this.neednum = neednum;
this.godown = godown;
}
publicvoid run() {
//消费指定数量的产品
godown.consume(neednum);
}
}
结果:
已经生产了10个产品,现仓储量为40
已经生产了10个产品,现仓储量为50
已经消费了50个产品,现仓储量为0
已经生产了80个产品,现仓储量为80
已经消费了30个产品,现仓储量为50
已经生产了10个产品,现仓储量为60
已经消费了20个产品,现仓储量为40
已经生产了10个产品,现仓储量为50
已经生产了10个产品,现仓储量为60
已经生产了10个产品,现仓储量为70
3、线程池
1.线程池相关API:ExecutorService和Executors
2.ExecutorService:真正的线程池接口。常用子类ThreadPoolExecutor。
1)执行任务/命令,没有返回值,一般执行Runnable
void execute(Runnable command)
2)关闭线程池
void shutdown()
3)设置线程池的大小
ExecutorService newFixedThreadPool(int nThreads)
3.Executors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池
public class ThreadPoolTest {
public static void main(String[] args) {
//1、创建服务,创建线程池
//newFixedThreadPool,参数为线程池大小
ExecutorService service = Executors.newFixedThreadPool(10);
//2、执行
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
//3、关闭连接池
service.shutdown();
}
}
class MyThread implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
运行结果:
pool-1-thread-1
pool-1-thread-4
pool-1-thread-3
pool-1-thread-2
pool-1-thread-5
运行结果:pool-1-thread-1
pool-1-thread-4
pool-1-thread-3
pool-1-thread-2
pool-1-thread-5