进程就是正在进行中的程序,线程就是进程中一个负责程序执行的控制单元。
一个进程中可以有多个执行路径,称为多线程。一个进程中至少要有一个线程。
电脑只有一个CPU的情况为什么可以同时执行多个任务呢? 其实电脑同一个时间点只能执行一个任务,因为切换速度快所以让我们觉得电脑能够同一个时间执行多个任务。 多线程的好处就是解决了多代码同时运行的问题,可是随着带来的缺点就是线程太多会导致执行速度变慢,电脑超负荷运行。
线程的创建方式一:
继承Thread类:
代码大致框架是:
class 类名 extends Thread
{
方法1;
方法2;
...
public void run(){
//other code...
}
属性1;
属性2;
...
}
示例:
class Hello extends Thread //继承Thread类
{
public Hello(){
}
public Hello(String name){ //构造方法
this.name = name;
}
public void run(){ //重写run方法
for(int i=0;i<5;i++){
System.out.println(name+"运行 "+i);
}
}
public static void main(String[] args)
{
Hello h1 = new Hello("A"); //创建对象
Hello h2 = new Hello("B");
h1.run();
h2.run();
}
private String name;
}
运行结果:
如上图,发现是按照顺序执行的,说明调用的方法不对,应该调用的start()方法。
public static void main(String[] args)
{
Hello h1 = new Hello("A"); //创建对象
Hello h2 = new Hello("B");
h1.start();
h2.start();
}
那么运行结果为:
两个线程轮流运行,不一定就是这个结果。
线程的创建方法二:
通过实现Runnable接口
大致框架:
class 类名 implements Runnable
{
方法1;
方法2;
...
public void run(){
//other code...
}
属性1;
属性2;
...
}
看个示例:
class Hello extends Thread //实现Runnable接口
{
public Hello(){
}
public Hello(String name){ //构造方法
this.name = name;
}
public void run(){ //重写run方法
for(int i=0;i<5;i++){
System.out.println(name+"运行 "+i);
}
}
public static void main(String[] args)
{
Hello h1 = new Hello("A"); //创建对象
Thread demo1 = new Thread(h1);
Hello h2 = new Hello("B");
Thread demo2 = new Thread(h2);
demo1.start();
demo2.start();
}
private String name;
}
其实Thread也是实现Runnable接口的:
class Thread implements Runnable
{
//...
public void run(){
if(target!=null){
target.run();
}
}
}
那么继承的时候选择哪个实现方式呢?
如果一个类继承了Thread,那么就不能再继承别的类了。所以建议用接口。
如何判断一个线程是否启动:
class Hello extends Thread //实现Runnable接口
{
public void run(){ //重写run方法
for(int i=0;i<3;i++){
System.out.println(Thread.currentThread().getName());
}
}
public static void main(String[] args){
Hello h = new Hello(); //创建对象
Thread demo = new Thread(h);
System.out.println("线程启动之前::"+demo.isAlive());
demo.start(); //启动线程
System.out.println("线程启动之后::"+demo.isAlive());
}
}
运行结果:
线程的状态:
可分为5种: 生、死、可运行、运行、等待\阻塞。
1.新状态:线程对象已经创建,不过还没有调用start()方法
2.可运行状态:当线程有资格运行,但调度程序还没有把它选定为运行线程时线程所处的状态
3.运行状态:线程调度程序从可运行池中选择一个线程作为当前线程时线程所处的状态
4.等待/阻塞/睡眠状态:三者的的共同点是:线程都是活的,不过没有条件运行
5.死亡状态:当线程的run()方法完成时就认为它死去了。线程一旦死亡就不能调用,否则会抛异常
关于线程的中断:
class Hello implements Runnable
{
public void run(){
System.out.println("执行run方法");
try
{
Thread.sleep(10000);
System.out.println("线程完成休眠");
}
catch (Exception e)
{
System.out.println("休眠被打断");
return; //返回到程序的调用处
}
System.out.println("线程正常终止");
}
public static void main(String[] args){
Hello h = new Hello();
Thread demo = new Thread(h,"线程");
demo.start();
try
{
Thread.sleep(2000); //休眠方法
}
catch (Exception e)
{
e.printStackTrace();
}
demo.interrupt(); //2s后中断线程
}
}
运行结果是:
/**
*线程的休眠
* */
class Hello implements Runnable {
public void run() {
for (int i = 0; i < 3; i++) {
try {
Thread.sleep(2000);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + i);
}
}
public static void main(String[] args) {
Hello he = new Hello();
Thread demo = new Thread(he, "线程");
demo.start();
}
}
关于多线程同步与锁的问题:
线程的同步是为了防止多个线程访问一个数据对象时,对数据造成破坏。
例如当两个线程ThreadA和ThreadB都操作同一个对象Book对象,并修改上面的数据:
class Shuju {
private int x = 100; //私有化变量
public int getX() { //对外提供public的get方法
return x;
}
public int fix(int y) {
x = x - y;
return x;
}
}
class MyRunnable implements Runnable {
private Shuju shuju =new Shuju(); //创建一个对象
public static void main(String[] args) {
MyRunnable r = new MyRunnable();
Thread ta = new Thread(r,"Thread-A"); //创建线程
Thread tb = new Thread(r,"Thread-B");
ta.start(); //开启start方法
tb.start();
}
public void run() { //重写run()方法
for (int i = 0; i < 3; i++) {
this.fix(30);
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " :当前Shuju对象的x值= " + shuju.getX());
}
}
public int fix(int y) {
return shuju.fix(y); //返回shuju中的y值
}
}
运行结果:
为了解决这个问题。我们必须规定同一个时间只能有一个线程来访问或者修改这个数据,这样才能保证数据的唯一性。
这时候我们就要用到锁。 一个对象一个对象只有一个锁。所以,如果一个线程获得该锁,就没有其他线程可以获得锁,直到第一个线程释放(或返回)锁。这也意味着任何其他线程都不能进入该对象上的synchronized方法或代码块,直到该锁被释放。
同步的好处:解决了线程的安全问题
同步的弊端:多了个锁,所以每个线程都需要去判断锁,会影响效率
同步的前提是多个线程使用同一个锁
class Ticket implements Runnable{
private int num = 100;
public void run(){
while(true ){
if(num > 0){
try{
Thread. sleep(10);
} catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "...sale..." + num--);
}
}
}
}
class TicketDemo{
public static void main(String[] args){
Ticket t = new Ticket();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
Thread t4 = new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
运行结果:
可以发现出现了负数的情况
修改后的代码:
class Ticket implements Runnable{
private int num = 100;
Object obj = new Object();
public void run(){
while(true ){
synchronized(obj ){
if(num > 0){
System.out.println(Thread.currentThread().getName() + "...sale..." + num--);
}
}
}
}
}
class TicketDemo{
public static void main(String[] args){
Ticket t = new Ticket();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
Thread t4 = new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
由于加了锁,所以运行结果不会出现负数的情况。
多线程下的单例模式:(重点、考点)
饿汉式:
class Single{
private static final Single s = new Single();
private Single(){}
public static Single getInstance(){
return s ;
}
}
因为饿汉式没有存在多个线程共享一个数据所以不需要锁。
懒汉式:
class Single{
private static Single s = null;
private Single(){}
public static Single getInstance(){
if(s ==null){
synchronized(Single.class){
if(s == null)
s = new Single();
}
}
return s ;
}
}
因为多个线程共享一个数据,所以需要用synchronized同步
再来看一个示例:
import java.util.concurrent.*;
import java.util.concurrent.locks.*;
/**
* Java线程:锁
*/
public class Test {
public static void main(String[] args) {
//创建并发访问的账户
MyCount myCount = new MyCount("95599200901215522", 10000);
//创建一个锁对象
ReadWriteLock lock = new ReentrantReadWriteLock(false);
//创建一个线程池
ExecutorService pool = Executors.newFixedThreadPool(2);
//创建一些并发访问用户,一个信用卡,存的存,取的取,好热闹啊
User u1 = new User("张三", myCount, -4000, lock, false);
User u2 = new User("李四", myCount, 6000, lock, false);
User u3 = new User("王五", myCount, -8000, lock, false);
User u4 = new User("张三", myCount, 800, lock,false);
User u5 = new User("李四", myCount, 0, lock,true);
//在线程池中执行各个用户的操作
pool.execute(u1);
pool.execute(u2);
pool.execute(u3);
pool.execute(u4);
pool.execute(u5);
//关闭线程池
pool.shutdown();
}
}
/**
* 信用卡的用户
*/
class User implements Runnable {
private String name; //用户名
private MyCount myCount; //所要操作的账户
private int iocash; //操作的金额,当然有正负之分了
private ReadWriteLock myLock; //执行操作所需的锁对象
private boolean ischeck; //是否查询
User(String name, MyCount myCount, int iocash, ReadWriteLock myLock,boolean ischeck) {
this.name = name;
this.myCount = myCount;
this.iocash = iocash;
this.myLock = myLock;
this.ischeck = ischeck;
}
public void run() {
if (ischeck) {
//获取读锁
myLock.readLock().lock();
System.out.println("读:" + name +"正在查询" + myCount +"账户,当前金额为" + myCount.getCash());
//释放读锁
myLock.readLock().unlock();
} else {
//获取写锁
myLock.writeLock().lock();
//执行现金业务
System.out.println("写:" + name +"正在操作" + myCount +"账户,金额为" + iocash +",当前金额为" + myCount.getCash());
myCount.setCash(myCount.getCash() + iocash);
System.out.println("写:" + name +"操作" + myCount +"账户成功,金额为" + iocash +",当前金额为" + myCount.getCash());
//释放写锁
myLock.writeLock().unlock();
}
}
}
/**
* 信用卡账户,可随意透支
*/
class MyCount {
private String oid; //账号
private int cash; //账户余额
MyCount(String oid, int cash) {
this.oid = oid;
this.cash = cash;
}
public String getOid() {
return oid;
}
public void setOid(String oid) {
this.oid = oid;
}
public int getCash() {
return cash;
}
public void setCash(int cash) {
this.cash = cash;
}
public String toString() {
return"MyCount{" +
"oid='" + oid + '\'' +
", cash=" + cash +
'}';
}
}
运行结果:
上面的示例用的是读写锁
关于死锁:
死锁是什么意思?
多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不可能正常终止。
当使用synchronized关键字时要注意。
死锁示例:
class Ticket implements Runnable{
private static int num = 100;
Object obj = new Object();
boolean flag = true;
public void run(){
if(flag ){
while(true ){
synchronized(obj ){
show();
}
}
} else
while(true )
show();
}
public synchronized void show(){
synchronized(obj ){
if(num > 0){
try{
Thread. sleep(10);
} catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "...function..." + num--);
}
}
}
}
class DeadLockDemo{
public static void main(String[] args){
Ticket t = new Ticket();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
t1.start();
try{
Thread. sleep(10);
} catch(InterruptedException e){
e.printStackTrace();
}
t. flag = false ;
t2.start();
}
}
这个死锁的产生就是同步的嵌套引起的。