多线程笔记
多线程的概述
多线程实现 1
通过重写run方法来使用多线程,多线程里面代码一般都是比较耗时的
public class fbb extends Thread {
@Override
public void run() {
for (int i=0;i<10;i++){
System.out.println(100);
}
}
}
如果要使用多线程就需要通过start方法 使线程执行,因为run方法是仅仅是封装被线程执行的代码。
fbb s1 = new fbb();
s1.start();
//Exception in thread "main" java.lang.IllegalThreadStateException
//同一个线程只能调用一次没办法调用两次
fbb s2 = new fbb();
//可以通过创建两个对象来调用两个线程
s2.start();
获取线程的名字与修改
@Override
public void run() {
for (int i=0;i<1000;i++){
System.out.println(i+" "+getName());
}
}
fbb s1 = new fbb();
s1.setName("线程1");
s1.start();
fbb s2 = new fbb();
//修改名字
s2.setName("线程2");
s2.start();
或者是通过他的构造方法进行改名
获取当前线程的线程名字
线程调度 优先级
优先级默认是5
线程休眠
public class fbb extends Thread {
@Override
public void run() {
for (int i=0;i<1000;i++){
System.out.println(i+" "+getName());
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
线程的加入
只有调用join的线程执行完毕后,后面的线程才会开始执行
礼让线程
public class fbb extends Thread {
@Override
public void run() {
for (int i=0;i<1000;i++){
System.out.println(i+" "+getName());
Thread.yield();
}
}
}
守护线程
当非守护线程停止,守护线程才停止.
中断线程
stop过时了不建议使用
public void interrupt(): 中断线程,并抛出InterruptedException,程序正常结束
多线程实现2 Runnable接口
public class fbb implements Runnable {
@Override
public void run() {
for (int i=0;i<10000;i++){
System.out.println(i+" "+Thread.currentThread().getName());//currentThread()方法返回正在被执行的线程的信息。
}
}
}
public class Main {
public static void main(String[] args) throws InterruptedException {
fbb s1 = new fbb();
Thread t1 = new Thread(s1,"线程1");
Thread t2 = new Thread(s1,"线程2");
t1.start();
t2.start();
}
}
两个多线程实现方法的区别图解
如何解决线程安全问题
问题例子卖票
public class fbb implements Runnable {
private static int tick=100;
@Override
public void run() {
while (true) {
if (tick>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" "+(tick--)+"票");
}
}
}
}
public class Main {
public static void main(String[] args) throws InterruptedException {
fbb s1 = new fbb();
Thread t1 = new Thread(s1,"窗口1");
Thread t2 = new Thread(s1,"窗口2");
Thread t3 = new Thread(s1,"窗口3");
t1.start();
t2.start();
t3.start();
}
}
解决线程问题的实现1 同步代码
public class fbb implements Runnable {
private int tick=1000;
private Object obj = new Object();
@Override
public void run() {
while (true) {
synchronized (obj){//使用同步代码块把他们给包起来
if (tick>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" "+(tick--)+"票");
}else {
return;
}
}
}
}
}
解决线程问题的实现2 接口Lock
package com.atguigu.com;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class fbb implements Runnable {
private int tick=50;
private Lock lock = new ReentrantLock();//ReentrantLock是Lock的实现类
@Override
public void run() {
while (true) {
//在这加上锁
try {
//加锁
lock.lock();
if (tick > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " " + (tick--) + "票");
} else {
return;
}
}finally {
//释放锁
lock.unlock();
}
}
}
}
同步的好处和弊端
同步代码还可以放在方法上
同步方法就是this因为每个方法内都有一个隐藏的this对象
可以用class创建对象但是要是当前的class文件
让一个集合变成线程安全的集合
死锁问题
理想状态
不理想状态
问题代码分析
因为他们两个对象都进入到了synchronized 的"门"中 然后在进入下一个synchronized的"门"时无法访问 导致成为了死锁。
等待唤醒
@Override
public void run() {
while (true){
synchronized ( s){
if (!s.falg){//如果不是空也就是修改完成数据了让他输出
try {
s.wait();//他醒了的时候也是从这里开始的
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(s.age+" "+s.name);
s.falg=false;
s.notify();
}
}
}
@Override
public void run() {
while (true){
synchronized (s){
if(s.falg){
try {
s.wait();//如果是true让他进行等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if(x%2==0){
s.age=13;
s.name="林超贤";
}else {
s.age=133;
s.name="林超1贤";
}
x++;
s.falg=true;//修改完成了就可以让他去输出访问
s.notify();//唤醒
}
}
多线程的抓换图和运行情况
线程组的概述和使用
线程的默认情况的Main组
修改线程所在的组
Student s =new Student();
Studendemo s1 = new Studendemo(s);
SetSteb s2 = new SetSteb(s);
ThreadGroup g1 = new ThreadGroup("彭于晏的线程组");//创建一共组
Thread t1 = new Thread(g1,s1,"吴亦凡");//把他们分到新建的组里面
Thread t2 = new Thread(g1,s1,"彭于晏");
System.out.println(t1.getThreadGroup().getName());
线程组的好处
可以通过组名称设置后台线程,表示该组的线程都是后台线程守护线程
线程池
能够极大的保证线程的效率
用完线程后还可以回收。
用Executors产生线程池
线程池的好处
package com.company;
import com.sun.org.apache.bcel.internal.generic.FSUB;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Main {
public static void main(String[] args) {
//创建一个线程池对象 控制要创建几个线程
ExecutorService pool = Executors.newFixedThreadPool(2);
//创建可执行Runnable或者Callable对象
pool.submit(new Runnable());
pool.submit(new Runnable());
}
}
如果想要关闭线程不让他回收
pool.submit(new Runnable());
pool.shutdown();//停止
多线程实现3 Callable
Callable 是一个带泛形的接口
//这里指的泛形其实是指call的返回值
public class CallableTEX implements Callable {
@Override
public Object call() throws Exception {
System.out.println("范冰冰"+""+Thread.currentThread().getName());
return null;
}
}
在通过线程池进行调用,但是他依赖线程池调用,如果需要用返回值的案例的话可以用线程3.
多线程方式实现求和
用此方法获取返回值
public class Runnable implements Callable {
private int number;
public Runnable(int number){
this.number=number;
}
@Override
public Object call() throws Exception {
int sum=0;
for (int x=1;x<=number;x++){
sum+=x;
}
return sum;
}
}
//创建一个线程池对象 控制要创建几个线程
ExecutorService pool = Executors.newFixedThreadPool(2);
//创建可执行Runnable或者Callable对象
Future<Integer>fu = pool.submit(new Runnable(100));
Integer i1 = fu.get();
System.out.println(i1);
pool.shutdown();
定时器
使用结束任务优化
多任务
连环爆炸,后面的2000就是前面3000执行后每隔2秒执行一次