线程开启不一定立即执行,由CPU调度执行
三种创建方式
1.继承Thread类(重点)
1.声明一个类继承为Thread的子类 2.子类重写Thread类中的run方法 3.创建线程对象,调用start()方法启动线程
Public class thread extends Thread{
@Override
public void run(){
//run方法线程体
}
public static void main(String[] args){
Thread thread = new Thread();
thread.start();
//可简化为 new Thread().start();
}
}
2.实现Runnable接口(重点 推荐使用,方便灵活,方便同一个对象被多个线程使用)
1.定义MyRunnable类实现Runnable接口 2.实现run()方法,编写线程执行体 3.创建线程对象,调用start()方法启动线程
Public class MyRunnable implements Runnable{
@Override
public void run(){
//run方法线程体
}
public static void main(String[] args){
//创建Runnable接口的实现类对象
MyRunnable myrunnable = new MyRunnable();
//创建线程对象,通过线程对象来开启我们的线程(代理)
Thread thread = new Thread(myrunnable);
thread.start();
//可简化为 new Thread(myrunnable).start();
}
}
3.实现Callable接口(了解)
Lamda表达式:
lamda表达式只能应用于函数式接口(接口中只能有一个抽象方法)
//定义一个接口
public interface ILove{
void Love(int a);
}
//lamda表达式
public static void main(String[] args){
ILove love = null;
//1.lamda表达式简化
love = (int a)->{
System.out.println("I Love You");
};
//2.简化参数类型(如果有一个参数的话可以去掉括号)
love = (a)->{
System.out.println("I Love You");
};
//3.简化为一行
love = (a)->System.out.println("I Love You");
}
总结:
1.Lamda表达式只能有一行代码的情况下才可以简化为一行,如果存在多行,那么只能有代码块包裹
2.使用lamda表达式的前提是必须是函数式接口
3.多个参数也可以去掉参数类型,要去掉就都去掉,但必须加上括号
2. 线程的状态
setPriority(int newPriority):更改线程的优先级
static void sleep(long millis):指定线程休眠
void join():插队线程
static void yield():暂停当前正在执行的线程对象,并执行其他线程
boolean isAlive():测试线程是否处于活动状态
停止线程
不推荐使用JDK提供的stop(),destroy()方法。(已经废弃)
推荐线程自己停止下来,建议使用一个标志位进行终止变量,当flag=false,则终止线程运行。
class TestStop implements Runnable{
private boolean flag = true;
@Verride
public void run(){
int i=0;
while(flag){
//输出i的值,并自加
}
}
//线程停止方法
void stop(){
this.flag = flag;
}
public static void main(String[] args){
//开启线程,去执行run方法
TestStop teststop = new TestStop();
new Thread(teststop).start();
//main()线程
for(int i=0;i<999;i++){
if(i==900){
//设置标志位,停止线程
teststop.stop();
}
}
}
}
线程礼让(yield)
礼让线程,让当前正在执行的线程暂停,但不阻塞
将线程从运行状态转化为就绪状态
让CPU重新调度,礼让不一定成功
public class TestYield{
public static void main(String[] args){
MyYield myYield = new MyYield();
new Thread(myYield ).start();
new Thread(myYield ).start();
}
}
class MyYield implements Runnable{
@Override
public void run(){
System.out.println(Thread.currentThread().getName()+"线程开始了");
Thread.yield();
System.out.println(Thread.currentThread().getName()+"线程结束了");
}
}
线程强制执行(join)
Join合并线程,待此线程执行完成后,再执行其他线程,其他线程堵塞(可以想象为插队)
class MyJoin implements Runnable{
@Override
public void run(){
system.out.println("VIP");
}
}
public static void main(String[] args){
MyJoin myJoin = new MyJoin ();
Thread thread = new Thread(myJoin);
thread.start();
for(int i=0;i<1000;i++){
if(i==200){
thread.join();
}
system.out.println("main"+i);
}
}
}
线程优先级(先设置再启动):
线程优先级用数字表示,范围从1~10 Thread.MIN_PRIORITY = 1 Thread.MAX_PRIORITY = 10 Thread.NORM_PRIORITY = 5 使用以下方式改变或获取优先级: 得到当前进程的优先级:getPriority() 设置进程的优先级:setPriority(int xxx)
守护线程(daemon)
1.线程分为用户线程和守护线程(一般情况不指定线程的前提下都是用户线程)
2.虚拟机必须确保用户线程执行完毕
3.虚拟机不用等待守护线程执行完毕
4.如:后台记录操作日志,监控内存,垃圾回收等
class God implements Runnable{
@Override
public void run(){
System.out.println("上帝永远守护着你");
}
}
class You implements Runnable{
@Override
public void run(){
for(int i=0;i<36500;i++){
System.out.println("今天也很快乐");
}
System.out.println("Goodbye this world!");
}
}
class TestDaemon{
public static void main(String[] args){
God god = new God();
You you = new You();
//开启守护线程
Thread thread = new Thread(god);
god.setDaemon(true); //默认是false表示是用户线程,正常的线程都是用户线程
god.start();
//开启用户线程
new Thread(you).start();
}
}
3.线程的同步(安全性)
不同线程对象卖出了重复的票甚至还出现负数票,这在日常的买票系统中是绝对不能允许的。
出现复票和负票的原因:
复值原因:当执行到输出 当前线程执行对象的时候,一个线程对象执行完输出语句后,还未执行num–语句时,第二个线程对象紧接着也执行到了该输出语句,这就导致了出现复票的原因。
负值原因:当num等于0时,一个线程对象执行完输出语句后,再执行完num-- 语句,第二个线程对象紧接着也执行到了输出语句,这时先执行的线程对象因为已经执行了num自减语句,此时num已经变为-1,紧跟第一个线程对象其后的第二个线程对象输出的是 num等于 -1,这就是出现负值的原因。
需要队列和锁
由于同一进程的锁哥线程共享同一块存储空间,在带来方便的同时也带来了访问冲突的问题,为了确保数据在方法中被访问时的正确性,在访问时加入锁机制synchronized,当一个线程获得对象的排它锁,则会独占资源,其他线程必须等待,使用后释放锁即可。
但会存在以下问题:
1.一个线程持有锁会导致其他所有需要此锁的线程挂起
2.在多线程竞争下,加锁,释放锁会导致比较多的上下文切换和调度延时,引起性能问题
3.如果一个优先级高的线程等待一个优先级低的线程释放锁,会导致优先级倒置,引起性能问题
synchronized锁
1.同步块synchronized(Obj){}
obj称之为同步监听器
1.obj可以是任何对象,但是推荐使用共享资源作为同步监听器
2.同步方法中无需指定同步监视器,因为同步方法的同保护监视器就是this,就是这个对象本身,或者class
2.同步方法public synchronized void method(int args){}
synchronized 方法控制对“对象”的访问,每个对象对应一把锁,每个synchronized 都必须获得调用该方法的对象的锁才能执行,否者线程会阻塞
public class UnSafeList {
public static void main(String[] args) {
List list = new ArrayList<>();
for (int i = 0; i < 10000; i++) {
new Thread(()->{
//添加一个锁,让线程排队
synchronized (list){
list.add(Thread.currentThread().getName());
}
}).start();
}
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(list.size());
}
}
Lock锁:(加锁:lock.Lock() 解锁:lock.unlock())
ReentrantLock类实现了Lock,他拥有与synchronized相同的并发性和内存语义,在实现线程安全的控制中,比较常见的是ReentrantLock,可以显示加锁、释放锁。
import java.util.concurrent.locks.ReentrantLock;
public class TestLock implements Runnable{
//定义锁
private final ReentrantLock lock = new ReentrantLock();
int ticketNums = 10;
@Override
public void run() {
while(true){
try {
lock.lock();//加锁
if (ticketNums > 0){
System.out.println(ticketNums--);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
break;
}
}finally {
lock.unlock();//解锁,解锁一般写在finally中
}
}
}
public static void main(String[] args) {
TestLock testLock = new TestLock();
new Thread(testLock).start();
new Thread(testLock).start();
new Thread(testLock).start();
}
}
4.线程的通信问题synchronized与Lock的区别:
1.Lock是显示锁(手动开启和关闭锁)synchronized是隐式锁,出了作用域自动释放
2.Lock只有代码块锁,synchronized有代码块锁和方法锁
3.使用lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类)
java提供几个方法解决线程之间的通信问题
wait():表示线程一直等待,知道其他线程通知,与sleep不同,会释放锁。
wait(long timeout):指等待的毫秒数。
notify():唤醒一个处于等待状态的线程
notifyAll():唤醒同一个对象上所有调用wait()方法的线程,优先级别高的线程优先调度
//生产者与消费者的问题
public class TestPC {
public static void main(String[] args) {
SynContainer container = new SynContainer();
Produect produect = new Produect(container);
Consumer consumer = new Consumer(container);
new Thread(produect).start();
new Thread(consumer).start();
}
}
//生产者
class Produect implements Runnable{
SynContainer container;
public Produect(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;
public Consumer(SynContainer container){
this.container = container;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("消费了---》"+container.pop().id+"只鸡");
}
}
}
class Chicken{
int id;
public Chicken(int id) {
this.id = id;
}
}
//容器
class SynContainer{
//需要一个容器大小
Chicken[] chickens = new Chicken[10];
int count = 0;
//生产者放入产品
public synchronized void push(Chicken chicken){
if (count == chickens.length){
//生产者等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果没有满,我们就需要丢入产品
chickens[count] = chicken;
count++;
//可以通知消费者可以消费了
this.notifyAll();
}
//消费者消费产品
public synchronized Chicken pop(){
//判断能否消费
if (count == 0){
//消费者等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果可以消费
count--;
Chicken chicken = chickens[count];
//吃完了,通知生产者生产
this.notifyAll();
return chicken;
}
}
5.线程池
提供的线程池API:ExecutorService和Executors
ExecutorService:真正的线程池接口,常见的子类ThreadPoolExecutor
void execute(Runnable command):执行任务/命令,没有返回值,一般用来执行Runnable
void shutdown():关闭连接池
Executors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池
// 创建单一线程的线程池
public static ExecutorService newSingleThreadExecutor();
// 创建固定数量的线程池
public static ExecutorService newFixedThreadPool(int nThreads);
// 创建带缓存的线程池
public static ExecutorService newCachedThreadPool();
// 创建定时调度的线程池
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize);
// 创建流式(fork-join)线程池
public static ExecutorService newWorkStealingPool();
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TestPool {
public static void main(String[] args) {
//1.创建服务,创建线程池
//参数为线程池的大小
ExecutorService executorService = Executors.newFixedThreadPool(5);
//给线程池100个线程任务
for (int i = 0; i < 100; i++) {
MyThread myThread = new MyThread();
executorService.execute(myThread);
}
//2.关闭连接
executorService.shutdown();
}
}
class MyThread implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getId());
}
}



