java线程基础

1.线程的特点

进程是由一个或多个线程组成的
1.抢占式运行【重要】
给程序分配CPU,按照时间来执行,单位时间片抢占式执行
2.资源共享
同一进程,有多个线程,这多个线程是可以共享同一数据的

2.线程的几种状态

1.新建(New)
创建后尚未启动。
2.可运行(Runnable)
可能正在运行,也可能正在等待 CPU 时间片。
包含了操作系统线程状态中的 Running 和 Ready。
3.阻塞(Blocking)
等待获取一个排它锁,如果其线程释放了锁就会结束此状态。
4.等待(Waiting)
等待其它线程显式地唤醒,否则不会被分配 CPU 时间片。
5.死亡(Terminated)
可以是线程结束任务之后自己结束,或者产生了异常而结束。

3.线程的创建方式

1.继承Thread方法

需要实现 run() 方法,因为 Thread 类也实现了 Runable 接口。
当调用 start() 方法启动一个线程时,虚拟机会将该线程放入就绪队列中等待被调度,当一个线程被调度时会执行该线程的 run() 方法。

package com.qf.xiancheng;

class Test  extends Thread{
    @Override
    public void run() {
        System.out.println("线程创建");
    }
}

public class Demo01 {
    public static void main(String[] args) {
        Test test = new Test();
        test.run();

        new Thread(() -> System.out.println("lambda表达式")).start();
    }
}

2.实现Runnable方法(最常用)

需要实现 run() 方法。

通过 Thread 调用 start() 方法来启动线程。

package com.qf.xiancheng;

class Test1 implements Runnable{
    @Override
    public void run() {
        System.out.println("实现Runnable方法的线程");
    }
}
public class Demo02 {
    public static void main(String[] args) {
        Test1 test1 = new Test1();
        test1.run();

        ((Runnable) () -> System.out.println("Lambda方法")).run();
    }
}

3.实现 Callable 接口

与 Runnable 相比,Callable 可以有返回值,返回值通过 FutureTask 进行封装。

package com.qf.xiancheng;

import java.util.concurrent.Callable;

class Test2 implements Callable<Integer>{

    @Override
    public Integer call() throws Exception {
        return 233;
    }
}
public class Demo03 {
    public static void main(String[] args) throws Exception {
        Test2 test2 = new Test2();
        System.out.println(test2.call());
    }
}

实现接口 VS 继承 Thread

实现接口会更好一些,因为:
Java 不支持多重继承,因此继承了 Thread 类就无法继承其它类,但是可以实现多个接口;
类可能只要求可执行就行,继承整个 Thread 类开销过大。

4.线程Thread的方法

Thread()
分配一个Thread对象

Thread(Runnable target)
分配一个新的Thread对象。

Thread(Runnable target,String name)
分配一个新的Thread对象,并对这个线程起一个名字

方法:

返回类型方法说明
static ThreadcurrentThread()返回对当前正在执行的线程对象的引用。
StringgetName()返回此线程的名称。
voidsetName(String name)将此线程的名称更改为等于参数 name
intgetPriority()返回此线程的优先级。
voidsetPriority(int newPriority)更改此线程的优先级。
static voidsleep(long millis)使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行),具体取决于系统定时器和调度程序的精度和准确性。
package com.qf.xiancheng;

import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer;

class Test3 implements Runnable {
    @Override
    public void run() {
        Thread thread = Thread.currentThread();//获取当前线程的名字(系统默认)
        System.out.println(thread);//打印
    }
}

class Test4 implements Runnable{
    @Override
    public void run() {
        System.out.println("test4");
        String name=Thread.currentThread().getName();
        System.out.println(name);
    }
}
public class Demo04 {
    public static void main(String[] args) {
        Thread thread = new Thread(new Test3());
        thread.start();

        Thread test4 =new Thread(new Test4());
        test4.setName("自己定义的线程名字");
        test4.start();

        Thread thread1=new Thread(new Test3(),"线程名");
        System.out.println(thread1.getName());

        Thread thread2=new Thread(new Test4());
        System.out.println(thread2.getPriority());//获取线程的优先级,默认为5

        Thread thread3 = new Thread(new Test3());
        thread3.setPriority(8);//自定义线程优先级1到10,数越大优先级越高,但是执行时仍然是抢占式的
        System.out.println(thread3.getPriority());

    }
}
package com.qf.xiancheng;

class Test5 implements Runnable{

    @Override
    public void run() {
        try {
            Thread.sleep(10000);//sleep只能用try-catch不能throw
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("先睡10秒才会输出");
    }
}

public class Demo05 {
    public static void main(String[] args) {
        Thread thread = new Thread(new Test5());
        thread.start();
    }
}

5.线程同步和锁

当多个线程同时运行时,线程的调度由操作系统决定,程序本身无法决定。因此,任何一个线程都有可能在任何指令处被操作系统暂停,然后在某个时间段后继续执行。
这个时候,有个单线程模型下不存在的问题就来了:如果多个线程同时读写共享变量,会出现数据不一致的问题。

线程同步模拟:

package com.qf.xiancheng;
//这是一个模拟线程同步的例子
//没有对线程加锁,两个窗口出售100张票
//不加锁会出现错误情况
class SaleTicket 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 class Demo06 {
    public static void main(String[] args) {
        //两个线程操作同一个数据
        Thread thread = new Thread(new SaleTicket());

        new Thread(thread,"线程1").start();
        new Thread(thread,"线程2").start();
        /*
        线程1卖出了第100张票
        线程2卖出了第100张票
        线程1卖出了第99张票
        线程2卖出了第98张票
         */
    }
}

通过上面的例子可以发现,虽然是两个线程操作统一数据,但是却会出现,了两个窗口卖出了同一张票。当线程1还未进行ticket–时,线程2此时获得了ticket
对线程进行加锁,保证同一时间只有一个线程可以对数据进行操作
解决办法:将买票的代码写到synchronized中

同步代码块:  将一段代码放到synchronized   然后括起来。就会对这段代码加上锁。
synchronized (this) {
			
}
package com.qf.xiancheng;
//这是一个模拟线程同步的例子
//没有对线程加锁,两个线程出售100张票
//不加锁会出现错误情况
class SaleTicket1 implements Runnable{
    private int ticket=100;
    @Override
    public void run() {
        while (true){
            synchronized (this){
                if (ticket>0){
                    System.out.println(Thread.currentThread().getName()+"卖出了第"+ticket+"张票");
                    ticket--;
                }else {
                    System.out.println("票卖完了");
                    break;
                }
            }
        }
    }
}

public class Demo07 {
    public static void main(String[] args) {
        //两个线程操作同一个数据
        Thread thread = new Thread(new SaleTicket1());

        new Thread(thread,"线程1").start();
        new Thread(thread,"线程2").start();
    }
}

synchronized 被成为隐式锁,会自动释放,是非公平的锁。
Lock锁 被称为显示锁。
他们两个锁都可以解决线程同步的问题。但是synchronized 更加灵活。所以一般开发时候用synchronized 。以后还会有线程池,也有锁更高级。
Lock是一个接口,实现ReentrantLock
有两个重要方法
​ lock();
​ unlock();

package com.qf.d_thread;

import java.util.concurrent.locks.ReentrantLock;

class SaleTicket1 implements Runnable {
    private int ticket = 100;
    ReentrantLock lock = new ReentrantLock();
    @Override
    public void run() {

        while (true) {
            try{


                lock.lock();
                if (ticket > 0) {
                    System.out.println(Thread.currentThread().getName() + "卖出了第" + ticket + "票");

                    ticket--;
                } else {
                    System.out.println("票已经买完了");
                    break;
                }
            } catch (Exception e) {

            } finally {
                lock.unlock();
            }
        }
    }
}
public class Demo2 {
    public static void main(String[] args) {
        //强调的是:多个线程操作同一个数据  ticket
        SaleTicket1 saleTicket = new SaleTicket1();

        new Thread(saleTicket, "线程1").start();
        new Thread(saleTicket, "线程2").start();
    }
}

在Java 中,死锁(Deadlock)情况是指:两个或两个以上的线程持有不同系统资源的锁,线程彼此都等待获取对方的锁来完成自己的任务,但是没有让出自己持有的锁,线程就会无休止等待下去。 线程竞争的资源可以是:锁、网络连接、通知事件,磁盘、带宽,以及一切可以被称作“资源”的东西

import java.util.Date;
 
public class LockTest {
   public static String obj1 = "obj1";
   public static String obj2 = "obj2";
   public static void main(String[] args) {
      LockA la = new LockA();
      new Thread(la).start();
      LockB lb = new LockB();
      new Thread(lb).start();
   }
}
class LockA implements Runnable{
   public void run() {
      try {
         System.out.println(new Date().toString() + " LockA 开始执行");
         while(true){
            synchronized (LockTest.obj1) {
               System.out.println(new Date().toString() + " LockA 锁住 obj1");
               Thread.sleep(3000); // 此处等待是给B能锁住机会
               synchronized (LockTest.obj2) {
                  System.out.println(new Date().toString() + " LockA 锁住 obj2");
                  Thread.sleep(60 * 1000); // 为测试,占用了就不放
               }
            }
         }
      } catch (Exception e) {
         e.printStackTrace();
      }
   }
}
class LockB implements Runnable{
   public void run() {
      try {
         System.out.println(new Date().toString() + " LockB 开始执行");
         while(true){
            synchronized (LockTest.obj2) {
               System.out.println(new Date().toString() + " LockB 锁住 obj2");
               Thread.sleep(3000); // 此处等待是给A能锁住机会
               synchronized (LockTest.obj1) {
                  System.out.println(new Date().toString() + " LockB 锁住 obj1");
                  Thread.sleep(60 * 1000); // 为测试,占用了就不放
               }
            }
         }
      } catch (Exception e) {
         e.printStackTrace();
      }
   }
}

上面的代码会产生死锁。

6.什么是线程死锁

死锁是指两个或两个以上进程(线程)在执行过程中,由于竞争资源或由于彼此通信造成的一种堵塞的现象,若无外力的作用下,都将无法推进,此时的系统处于死锁状态。

7.守护线程

线程分为用户线程和守护线程

虚拟机必须确保用户线程执行完毕

虚拟机不用等待守护线程执行完毕

​ 如,后台记录操作日志,监控内存,垃圾回收等待。。。

守护线程是用来守护非守护线程的,当非守护线程结束时,守护线程也会马上结束

package com.qf.xiancheng;

public class Demo08 {

    public static void main(String[] args) {
        God god = new God();
        You you = new You();
        Thread thread = new Thread(god);
        thread.setDaemon(true);//默认为flase 为用户线程,  true为守护线程
        thread.start();
        new Thread(you).start();
    }
}

class God implements Runnable {

    @Override
    public void run() {
        while (true) {
            System.out.println("上帝守护着你(守护线程)-------");
        }
    }
}

class You implements Runnable {

    @Override
    public void run() {
        for (int i = 0; i < 365; i++) {
            System.out.println("开心着活着每一天(非守护线程)------");
        }
        System.out.println("----goodbye!Beautiful World!!!------");
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值