1、创建线程的三种方式
- 继承Thread类
//线程实现方式一
public class ThreadDome01 extends Thread{
@Override
public void run() {
for (int i=0;i<20;i++){
System.out.println("看小说"+i);
}
}
public static void main(String[] args) {
//开启ThreadDome01线程
new ThreadDome01().start();
for (int i=0;i<2000;i++){
System.out.println("主线程执行"+i);
}
}
}
- 实现Runnable接口
public class RunnableDome01 implements Runnable{
@Override
public void run() {
for(int i = 0;i<20;i++){
System.out.println("runnable实现线程"+i);
}
}
public static void main(String[] args) {
//开启实现Runnable接口的线程
RunnableDome01 dome01 = new RunnableDome01();
new Thread(dome01).start();
for (int i = 0;i<200;i++){
System.out.println("main线程"+i);
}
}
}
- 实现Callable接口
例1:
public class CallableDome01 implements Callable<Boolean> {
@Override
public Boolean call(){
for(int i = 0;i<20;i++){
System.out.println("runnable实现线程"+i);
}
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
CallableDome01 t1 = new CallableDome01();
//1、开启服务
ExecutorService executor = Executors.newFixedThreadPool(3); //3为开启线程个数
//2、开启线程
Future<Boolean> f1 = executor.submit(t1);
//3、获取返回值
Boolean aBo1 = f1.get();
//4、关闭服务
executor.shutdown();
}
}
例2:
public class CallableDome02 {
public static void main(String[] args) {
FutureTask futureTask = new FutureTask(new MyThread());
new Thread(futureTask).start();
try {
Integer integer = (Integer) futureTask.get();
System.out.println(integer);
} catch (Exception e) {
e.printStackTrace();
}
}
}
class MyThread implements Callable{
@Override
public Integer call() throws Exception {
return 100;
}
}
2、线程方法和状态
-
sleep() 线程休眠
线程休眠,不会释放锁,会阻塞 -
yield() 礼让方法
对于同一个对象,开启的两个线程,进入cup中的线程调用yield()方法,会改变线程为就绪状态, 下一次两个线程谁先执行看cup心情 -
setpriority() 设置线程的优先级
优先级默认是5,权重大的优先执行 -
join() 线程插队
可以使插队的线程优先主线程执行 -
setDaemon(true) 设置线程为守护线程
Thread thread = new Thread(dome02);
thread.setDaemon(true);
3、线程同步
3.1、线程同步中死锁问题
- 什么是死锁:多个线程持有对方的资源,形成僵持
- 产生死锁的四个条件:
1、互斥条件:一个资源每次只能被一个线程访问
2、请求与保持条件:一个进程因请求资源而阻塞式,对以获得的资源保持不放
3、不剥夺条件:进程获得的资源,在未使用完成之前,不能被强行剥夺
4、循环等待条件 :若干个进程之间形成一种头尾相接的循环等待资源关系
线程死锁代码:
public class DeadLock {
public static void main(String[] args) {
new Thread(new TestThread(1)).start();
new Thread(new TestThread(0)).start();
}
}
class A{
}
class B{
}
class TestThread implements Runnable{
static A a = new A();
static B b = new B();
private int flag;
public TestThread(int flag){
this.flag = flag;
}
@Override
public void run() {
if (flag == 1){
synchronized (a){
System.out.println(Thread.currentThread().getName()+"获取的 a 对象的锁");
try {
Thread.sleep(1000); //模拟延迟,不让1一下拿到两个对象的锁
} catch (InterruptedException e) {
e.printStackTrace();
}
//1、死锁
/*synchronized (b){
System.out.println(Thread.currentThread().getName()+"获取的 b 对象的锁");
}*/
}
//2、解决死锁的方法
synchronized (b){
System.out.println(Thread.currentThread().getName()+"获取的 b 对象的锁");
}
}else if (flag == 0){
synchronized (b){
System.out.println(Thread.currentThread().getName()+"获取的 b 对象的锁");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//1、死锁
/*synchronized (a){
System.out.println(Thread.currentThread().getName()+"获取的 a 对象的锁");
}*/
}
//2、解决死锁
synchronized (a){
System.out.println(Thread.currentThread().getName()+"获取的 a 对象的锁");
}
}
}
}
3.2、实现线程同步
- synchronized 同步代码块
锁的是对象,一般是公共资源,可用于方法。
//使用同步代码块来解决Arraylist线程不安全问题
public class ListTest {
public static void main(String[] args) {
//arraylist是线程不安全的
List<String> list = new ArrayList<>();
synchronized (list){
for (int i = 0; i < 1000; i++) {
new Thread(()->{
list.add(Thread.currentThread().getName());
}).start();
}
}
try {
Thread.sleep(8000);
System.out.println(Thread.currentThread().getName());
}catch (Exception e){
e.printStackTrace();
}
System.out.println(list.size());
}
}
- lock锁
Reentrantlock 可重入所
public class LockDome {
public static void main(String[] args) {
Test01 test01 = new Test01();
new Thread(test01).start();
new Thread(test01).start();
new Thread(test01).start();
}
}
class Test01 implements Runnable{
private int i = 10;
private final ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true){
try {
lock.lock();
if (i>0){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
} System.out.println(Thread.currentThread().getName()+"拿到了第"+i--+"票");
}else {
break;
}
}finally {
lock.unlock();
}
}
}
}
4、线程通信
- 通过缓冲区(管程法)
/**
* 测试生产者和消费者模型—> 利用缓冲区解决:管程法
* 生产者、消费者、产品、缓存区
*/
public class TestPC {
public static void main(String[] args) {
SynContainer container = new SynContainer();
new Thread(new Productor(container)).start();
new Thread(new Consumer(container)).start();
}
}
//生产者
class Productor implements Runnable{
SynContainer container;
Productor(SynContainer container){
this.container = container;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
container.push(new Chicken(i));
System.out.println("生产了第"+i+"只鸡");
}
}
}
//消费者
class Consumer implements Runnable{
SynContainer container;
Consumer(SynContainer container){
this.container = container;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
container.pop(new Chicken(i));
System.out.println("消费了第"+i+"只鸡");
}
}
}
//产品
class Chicken{
private int id; //编号
Chicken(int id){
this.id = id;
}
}
//缓存区
class SynContainer{
//设置容器大小
Chicken[] chickens = new Chicken[10];
//容器计数器
int conut = 0;
//生产者生产放入产品
public synchronized void push(Chicken chicken){
if (conut == chickens.length){
//通知消费者消费
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
chickens[conut] = chicken;
conut++;
//通知消费者消费
this.notifyAll();
}
//消费者消费取出产品
public synchronized Chicken pop(Chicken chicken){
if (conut == 0){
//通知生产者生产
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
conut--;
Chicken chicken1= chickens[conut];
//吃完通知生产者生产
this.notifyAll();
return chicken1;
}
}
5、线程池
public class ThreadPool {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//1、创建线程池 newFixedThreadPool 参数为池子大小
ExecutorService service = Executors.newFixedThreadPool(5);
//2、执行线程
service.submit(new MyThread01());
service.submit(new MyThread01());
service.execute(new MyThread01());
Future submit = service.submit(new MyThread02());
Integer number = (Integer) submit.get();
System.out.println(number);
//3、关闭线程池
service.shutdown();
}
}
class MyThread01 implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"MyThread01");
}
}
class MyThread02 implements Callable {
@Override
public Integer call() throws Exception {
System.out.println(Thread.currentThread().getName()+"MyThread02");
return 11;
}
}
注意点:
- 线程池submit()方法 既可以执行Runnable接口 线程,也可执行 Callable 接口线程;
- 线程池execute()方法只能执行 Runnable接口 线程