创建
继承创建
package com.dto;
/**
* 多线程的创建
* 1、继承Thread类
* 2、重写里面的run方法 方法体的执行 线程需要做什么。
* 3、创建Thread的子类对象
* 4、使用对象调用start方法
*/
public class ThreadTest {
public static void main(String[] args) {
MyThread myThread=new MyThread();
myThread.start();
MyThread02 myThread02=new MyThread02();
myThread02.start();
}
}
class MyThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
if (i%2==0){
System.out.println(i);
}
}
}
}
class MyThread02 extends Thread{
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
if (i%2!=0){
System.out.println(i);
}
}
}
}
创建问题
只使用run方法 单纯的方法调用 没有使用线程
调用多次start会出现异常(源码中有声名)
查看线程的名字
使用匿名内部类 重写方法。
Thread类的常用方法
/** * start :启动线程 调用run方法 * run :执行线程的内容的操作 * currentThread :返回当前线程,Thread.crrentThread() [当线程执行到这里时就会放回当前的] * getName 对象名. * setName 对象名. (也可使用构造器得到 ) * yield :释放当前cpu给线程的时间 * join :线程1.join 放在线程2的执行里面 需要线程一执行完才可以 插队 插队执行完才可以 执行线程2 线程2阻塞. * stop :已过时 强制线程停止。 * sleep :睡眠多少毫秒 当前线程阻塞状态 * isAlive :是否存活 * getPriority :得到优先级 * setPriority :设置优先级 1——10 * 设置优先级需要在start开始之前 也只是概率高 也不是一定有 * */
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
if (i%2!=0){
System.out.println(this.getName()+" "+i);
}
if (i%3==0){
yield();
//this.yield();
//Thread.currentThread().yield();
}
}
}
public class ThreadTest {
public static void main(String[] args) {
MyThread myThread=new MyThread();
myThread.start();
MyThread02 myThread02=new MyThread02();
myThread02.start();
}
}
class MyThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
if (i%2==0){
System.out.println(i);
}
}
}
}
class MyThread02 extends Thread{
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
if (i%2!=0){
System.out.println(this.getName()+" "+i);
}
if (i%3==0){
MyThread myThread=new MyThread();
try {
myThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
class MyThread02 extends Thread{
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
if (i%2!=0){
System.out.println(this.getName()+" "+i);
}
if (i%3==0){
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
经典练习:窗口卖票
//每个线程都有100张票 其实总共300张
// 使用private static int ticket=100; 静态变量有一份 可以暂时解决。。
public class Window extends Thread{
private int ticket=100;
@Override
public void run() {
while (true){
if (ticket>0){
System.out.println(getName()+"卖票了"+"票号为:"+ticket);
ticket--;
}else {
System.out.println("卖完了");
break;
}
}
}
public static void main(String[] args) {
Window window=new Window();
Window window1=new Window();
Window window2=new Window();
window.setName("线程1");
window.start();
window1.start();
window2.start();
}
实现创建
卖票问题
可以直接用实例变量了 操作一个对象 所以数据可以100 开始但是有安全问题
package com.dto;
public class ThreadTest02 implements Runnable{
private int ticket=100;
@Override
public void run() {
while (true){
if (ticket>0){
System.out.println(Thread.currentThread().getName()+"卖票了"+"票号为:"+ticket);
ticket--;
}else {
System.out.println("卖完了");
break;
}
}
}
public static void main(String[] args) {
//创建线程的类
ThreadTest02 test02=new ThreadTest02();
//创建线程
Thread thread=new Thread(test02);
Thread thread01=new Thread(test02);
Thread thread02=new Thread(test02);
thread.start();
thread01.start();
thread02.start();
//线程里面有runnable 类型的target变量 当线程的run为null 使用了变量的run方法
}
}
两种创建方式的对比
线程的生命周期
线程state
public enum State {
/**
* Thread state for a thread which has not yet started.
*/
NEW,
/**
* Thread state for a runnable thread. A thread in the runnable
* state is executing in the Java virtual machine but it may
* be waiting for other resources from the operating system
* such as processor.
*/
RUNNABLE,
/**
* Thread state for a thread blocked waiting for a monitor lock.
* A thread in the blocked state is waiting for a monitor lock
* to enter a synchronized block/method or
* reenter a synchronized block/method after calling
* {@link Object#wait() Object.wait}.
*/
BLOCKED,
/**
* Thread state for a waiting thread.
* A thread is in the waiting state due to calling one of the
* following methods:
* <ul>
* <li>{@link Object#wait() Object.wait} with no timeout</li>
* <li>{@link #join() Thread.join} with no timeout</li>
* <li>{@link LockSupport#park() LockSupport.park}</li>
* </ul>
*
* <p>A thread in the waiting state is waiting for another thread to
* perform a particular action.
*
* For example, a thread that has called <tt>Object.wait()</tt>
* on an object is waiting for another thread to call
* <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
* that object. A thread that has called <tt>Thread.join()</tt>
* is waiting for a specified thread to terminate.
*/
WAITING,
/**
* Thread state for a waiting thread with a specified waiting time.
* A thread is in the timed waiting state due to calling one of
* the following methods with a specified positive waiting time:
* <ul>
* <li>{@link #sleep Thread.sleep}</li>
* <li>{@link Object#wait(long) Object.wait} with timeout</li>
* <li>{@link #join(long) Thread.join} with timeout</li>
* <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
* <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
* </ul>
*/
TIMED_WAITING,
/**
* Thread state for a terminated thread.
* The thread has completed execution.
*/
TERMINATED;
}
状态装换
状态改变之间可以书写代码 状态切换时会干什么 记住要点
线程安全问题
上面使用的多线程 对某个数据 操作 会出现线程安全问题
当线程出现睡眠是 变量可能被其他线程操作 最后出现 0 -1
安全问题:一个线程对数据没执行完其他线程操作了数据
解决安全问题 :加锁 拿到数据加锁
同步代码块实现线程同步
最初两种方式:
- 同步代码块(实现接口的方法)
- 同步方法
同步代码块
//同步监视器 俗称锁 :任何一个类的对象都可以 // 锁的要求:多个线程共用一把锁。 synchronized (同步监视器){ //需要被同步的代码 //既是操作共享数据的代码 //共享数据:多线程共享的变量 }
package com.dto;
public class ThreadTest02 implements Runnable{
private int ticket=100;
// 同一对象
Object object=new Object();
@Override
public void run() {
while (true){
synchronized (object){
if (ticket>0){
System.out.println(Thread.currentThread().getName()+"卖票了"+"票号为:"+ticket);
ticket--;
}else {
System.out.println("卖完了");
break;
}
}
}
}
public static void main(String[] args) {
//创建线程的类
ThreadTest02 test02=new ThreadTest02();
//创建线程
Thread thread=new Thread(test02);
Thread thread01=new Thread(test02);
Thread thread02=new Thread(test02);
thread.start();
thread01.start();
thread02.start();
//线程里面有runnable 类型的target变量 当线程的run为null 使用了变量的run方法
}
}
对象放在run里面的话 对象每次都创建一个就不是同一对象 也有问题
@Override
public void run() {
Object object=new Object();
while (true){
synchronized (object){
if (ticket>0){
System.out.println(Thread.currentThread().getName()+"卖票了"+"票号为:"+ticket);
ticket--;
}else {
System.out.println("卖完了");
break;
}
}
}
}
好处:
解决安全问题
坏处:
操作同步代码时 只能有一个线程操作 相当于单线程使用了。效率低
使用继承的同步代码块
使用继承时,因为都会创建Thread的接对象 里面的对象就不是唯一
package com.dto;
/*有问题的锁对象 每次创建线程的充当锁的不是一个对象*/
public class Window extends Thread{
private static int ticket=100;
Object object =new Object();
@Override
public void run() {
while (true){
synchronized (object){
if (ticket>0){
System.out.println(getName()+"卖票了"+"票号为:"+ticket);
ticket--;
}else {
System.out.println("卖完了");
break;
}
}
}
}
public static void main(String[] args) {
Window window=new Window();
Window window1=new Window();
Window window2=new Window();
window.setName("线程1");
window.start();
window1.start();
window2.start();
}
}
加static 使得只有一个 (注意操作的变量需要唯一 使用static修饰ticket)
this可以用在实现里面做锁监视器
在继承中每次的都是不一样的this
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
使用类.class可以成为同步监视器。类也是对象
得到一个class对象,使用类.class得到的对象是唯一的,因为类对象只加载一次
![](https://i-blog.csdnimg.cn/blog_migrate/91a8c4756d50396715569d13516d7994.png)
同步方法
将操作共享数据的方法做成同步方法、 返回值前面加synchronized。
实现中使用同步方法
@Override
public void run() {
while (true){
synchronized (Thread.class){
if (ticket>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName()+"卖票了"+"票号为:"+ticket);
ticket--;
}else {
System.out.println("卖完了");
break;
}
}
}
}
提取操作数据的代码为一个方法,加价关键字,run调用
方法的同步监视器为this
package com.dto;
public class ThreadTest02 implements Runnable{
private int ticket=100;
// 同一对象
//Object object=new Object();
@Override
public void run() {
Object object=new Object();
while (true){
extracted();
if (ticket==0){
break;
}
}
}
private synchronized void extracted() {
if (ticket>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"卖票了"+"票号为:"+ticket);
ticket--;
}else {
System.out.println("卖完了");
}
}
public static void main(String[] args) {
//创建线程的类
ThreadTest02 test02=new ThreadTest02();
//创建线程
Thread thread=new Thread(test02);
Thread thread01=new Thread(test02);
Thread thread02=new Thread(test02);
thread.start();
thread01.start();
thread02.start();
//线程里面有runnable 类型的target变量 当线程的run为null 使用了变量的run方法
}
}
继承中方法加synchronized 会出现问题,因为指定时当前 锁为 this 但是每次创建的线程this不是同一个。
需要加static 在synchronized前 监视器:this.class
双检锁实现线程安全的单例模式 懒汉式
这里锁对象也是this 但是线程操作同一个对象时都是它 所以没有安全问题 但是等同于单线程
效率慢
package com.dto;
public class CLASS {
public CLASS() {
}
//静态变量 保证只有一个惬意对象
private static CLASS aClass=null;
//同步方法
public static synchronized CLASS get(){
if (aClass==null){
aClass=new CLASS();
}
return aClass;
}
}
效率高的双检锁 不用线程等待了 所有线程都可以去到第一个判断
package com.dto;
public class CLASS {
public CLASS() {
}
//静态变量 保证只有一个惬意对象
private static CLASS aClass=null;
//同步方法
public static CLASS get(){
if (aClass == null){
synchronized(CLASS.class){
if (aClass==null){
aClass=new CLASS();
}
}
}
return aClass;
}
}
死锁
不同的的线程占用其他线程所需资源 等待对方释放资源。
死锁举例:【说一下什么是死锁 说完就录取你 / 录取我就告诉你】
下面会出现 但是效率比较低 可以加
package com.dto;
public class ThreadTest07 {
public static void main(String[] args) {
StringBuffer stringBuffer=new StringBuffer();
StringBuffer stringBuffer1 =new StringBuffer();
new Thread(){
@Override
public void run() {
synchronized (stringBuffer){
stringBuffer.append("1");
stringBuffer1.append("2");
synchronized (stringBuffer1){
stringBuffer.append(3);
stringBuffer1.append(4);
System.out.println(stringBuffer);
System.out.println(stringBuffer1);
}
}
}
}.start();
new Thread(new Runnable() {
@Override
public void run() {
synchronized (stringBuffer1){
stringBuffer.append("1");
stringBuffer1.append("2");
synchronized (stringBuffer){
stringBuffer.append(3);
stringBuffer1.append(4);
System.out.println(stringBuffer);
System.out.println(stringBuffer1);
}
}
}
}).start();
}
}
不死亡 不关闭什么也不执行
解决办法
Lock解决线程安全问题 jdk5.0
// 1、实例化 ReentrantLock lock =new ReentrantLock(); 参数 true 公平锁 flase不公平锁 不一定公平 还得等时间调度
//2、调用lock()方法 和操作资源的代码一起 写在try里面 为了在finally里面释放锁 lock.lock();
//3、解锁 写在finally 下面代码没有catch 是因为没有异常需要处理了。 lock.unlock();
package com.dto;
import java.util.concurrent.locks.ReentrantLock;
public class ThreadTest02 implements Runnable{
private int ticket=100;
// 1、实例化
ReentrantLock lock =new ReentrantLock(true);
@Override
public void run() {
Object object=new Object();
while (true){
try{
//2、调用lock()方法
lock.lock();
if (ticket>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"卖票了"+"票号为:"+ticket);
ticket--;
}else {
System.out.println("卖完了");
break;
}
}finally {
//3、解锁
lock.unlock();
}
//2、调用lock()方法
lock.lock();
if (ticket>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"卖票了"+"票号为:"+ticket);
ticket--;
}else {
System.out.println("卖完了");
break;
}
}
}
public static void main(String[] args) {
//创建线程的类
ThreadTest02 test02=new ThreadTest02();
//创建线程
Thread thread=new Thread(test02);
Thread thread01=new Thread(test02);
Thread thread02=new Thread(test02);
thread.start();
thread01.start();
thread02.start();
//线程里面有runnable 类型的target变量 当线程的run为null 使用了变量的run方法
}
}
与 synchronized (someObject) 类似的,lock()方法,表示当前线程占用lock对象,一旦占用,其他线程就不能占用了。
与 synchronized 不同的是,一旦synchronized 块结束,就会自动释放对someObject的占用。 lock却必须调用unlock方法进行手动释放,为了保证释放的执行,往往会把unlock() 放在finally中进行。
线程间的通信
循环打印100 一个线程打印一次
sleep 不释放锁
wait 释放锁
线程1 进去了方法锁起来执行 执行完 wait 然后释放锁 线程2可以进来 执行notify 接触线程1的wait状态进入就绪状态 线程2执行。
实现创建线程。
package com.dto;
import java.util.concurrent.locks.ReentrantLock;
public class ThreadTest02 implements Runnable{
private int ticket=100;
@Override
public void run() {
Object object=new Object();
while (true){
synchronized (this){
//解除线程的wait状态
notify();
if (ticket>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"卖票了"+"票号为:"+ticket);
ticket--;
}else {
System.out.println("卖完了");
break;
}
try {
//使线程进入阻塞状态 释放锁
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
//创建线程的类
ThreadTest02 test02=new ThreadTest02();
//创建线程
Thread thread=new Thread(test02);
Thread thread01=new Thread(test02);
Thread thread02=new Thread(test02);
thread.start();
thread01.start();
thread02.start();
//线程里面有runnable 类型的target变量 当线程的run为null 使用了变量的run方法
}
}
调用三个方法的对象必须是 同步监视器(锁对象) 在java.lang.object
所以使用继承方式的时候选取锁对象很重要。。。。。。。。。。。。。。。
Lock实现通信
(1条消息) 多线程 第八节 线程间通信之Lock_ellen艾琳的博客-CSDN博客
Sleep 和wait的区别
同:
都可以使线程进入阻塞状态
都需要捕获异常
异:
方法声明位置不同 一个在Thread下 ,一个在Object下
调用要求不同:sleep任何场景 wait同步代码块和同步方法中 调用对象必须是同步监视器
释放锁: sleep不释放 wait释放锁
经典例题 生产者消费者问题
学会分析
package com.dto;
/**clerk 店员
* 店员可以对生成者消费者做记账统计
*/
class Clerk {
private int count=0; //数量
//同步生产方法
public synchronized void produce() {
if (count<20){
count++;
System.out.println(Thread.currentThread().getName()+"开始生产第"+count+"产品");
//加加之后告诉消费者可以接触阻塞进来了
notify();
}else {
//>=20 不生产了
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
// 同上
public synchronized void consume() {
if (count>0){
System.out.println(Thread.currentThread().getName()+"开始消费第"+count+"产品");
count--;
notify();
}else {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//生产者 线程
class Producer extends Thread{
Clerk clerk;
//加构造器
public Producer(Clerk clerk){
this.clerk=clerk;
}
@Override
public void run() {
while (true){
clerk.produce();
}
}
}
// 消费者线程
class Consume extends Thread{
Clerk clerk;
//加构造器
public Consume(Clerk clerk){
this.clerk=clerk;
}
@Override
public void run() {
while (true){
clerk.consume();
}
}
}
//主方法
public class ThreadTest08 {
public static void main(String[] args) {
Clerk clerk=new Clerk();
Producer producer=new Producer(clerk);
Consume consume = new Consume(clerk);
producer.start();
consume.start();
}
}
创建多线程 :线程池
实现callable接口
run不可以抛异常 能try catch 不能看到出现什么问题。
package com.dto;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
// 1、实现
public class CallableTest implements Callable {
//2、重写
@Override
public Object call() throws Exception {
int sum=0;
for (int i = 0; i < 10; i++) {
sum+=i;
}
return sum;
}
public static void main(String[] args) {
// 3、创捷实现接口的对象
CallableTest callableTest = new CallableTest();
// 4、创建得到返回值的对象FutureTask
FutureTask futureTask=new FutureTask(callableTest);
// 5、新建线程传入 FutureTask 对象 对象实现了runnable接口
new Thread(futureTask).start();
// 6、想得到返回值 需要用 futureTask.get
try {
Object o = futureTask.get();
System.out.println("总和为"+o);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
1、得到的返回值可以交给其他线程去操作
2、实际的返回值因为线程Thread没有方法 实际用的就是FutrueTask的方法 做一个中间代理
3、实现Callable可以加泛型 就是对线程返回值做规定
线程池 :
线程池的好处:
避免频繁创建和销毁线程 消耗io资源
便于线程管理 池大小 最大线程数 线程没有任务保存时间
shutdown 关闭连接池
package com.dto;
import java.util.concurrent.*;
public class Test {
public static void main(String[] args) {
// 线程池创建 ExecutorService是接口 实际的作用是是实现类
ExecutorService executorService = Executors.newFixedThreadPool(10);
//线程池作用类
ThreadPoolExecutor service = (ThreadPoolExecutor) executorService;
service.setCorePoolSize(2);
//执行的参数还是我们自己定义线程
executorService.execute(new Runnable() {
@Override
public void run() {
}
});
try {
executorService.submit(new CallableTest()).get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
volatile关键字
public class Test {
public volatile int inc = 0;
public void increase() {
inc++;
}
public static void main(String[] args) {
final Test test = new Test();
for(int i=0;i<10;i++){
new Thread(){
public void run() {
for(int j=0;j<10;j++)
test.increase();
};
}.start();
}
System.out.println(test.inc);
}
}
结果可能 50 78 90 .。。。
没有保证原子性操作 执行完才结束
线程1 拿到数据没做计算阻塞 线程2拿到了计算了返回 线程1在计算 内存只得到了线程1的结果