• 128702

    文章

  • 810

    评论

  • 12

    友链

  • 最近新加了换肤功能,大家多来逛逛吧~~~~
  • 喜欢这个网站的朋友可以加一下QQ群,我们一起交流技术。

Java线程总结---第一天

服了这份高薪指南,涨多少你说了算>>

线程和进程各自有什么区别和优劣:

  • 进程是资源分配的最小单位,线程是程序执行的最小单位

  • 进程有自己的独立地址空间,每启动一个进程,系统就会为它分配地址空间,建立数据表来维护代码段、堆栈段和数据段,这种操作非常昂贵。而线程是共享进程中的数据的,使用相同的地址空间,因此CPU切换一个线程的花费远比进程要小很多,同时创建一个线程的开销也比进程要小很多,线程的上下文切换的性能消耗要小于进程。

  • 线程之间的通信更方便,同一进程下的线程共享全局变量、静态变量等数据。

  • 多进程程序更健壮,多线程程序只要有一个线程死掉,整个进程也死掉了,而一个进程死掉并不会对另外一个进程造成影响,因为进程有自己独立的地址空间。

并行与并发

并发是没有时间上的重叠的,两个任务是交替执行的,由于切换的非常快,对于外界调用者来说相当于同一时刻多个任务一起执行了;而并行可以看到时间上是由重叠的,也就是说并行才是真正意义上的同一时刻可以有多个任务同时执行。

小程序demo

public class Demo extends Thread {
    @Override
    public void run() {
        while (true){
            System.out.println(this.currentThread().getName());
        }
    }

    public static void main(String[] args) {
        Demo demo= new Demo();
        demo.start();
    }
}

public class Demo implements Runnable {
    @Override
    public void run() {
        while (true){
            System.out.println("hello thread");
        }
    }

    public static void main(String[] args) {
        Demo demo= new Demo();
        Thread thread=new Thread(demo,"t1");
        thread.start();
    }
}

public class Demo{

    public int count = 0;


    public  void print() {

        while (true){
            System.out.println(count++);
        }
    }

    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                new Demo().print();
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                new Demo().print();
            }
        }).start();
    }
}

synchronized锁范围

普通同步方法,锁是当前实例对象

静态同步方法,锁是当前类的class对象

同步方法块,锁是括号里面的对象

public class Demo{

    public synchronized void synsMethod1(){
        System.out.println("method1---");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        synsMethod3();
    }
    public synchronized  void  synsMethod2(){
        System.out.println("method2---");
    }

    public synchronized  void  synsMethod3(){
        System.out.println("method3---");
    }
    public static void main(String[] args) {
        Demo demo=new Demo();
        new Thread(new Runnable() {
            @Override
            public void run() {
                demo.synsMethod1();
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                demo.synsMethod2();
            }
        }).start();
    }
}

输出结果

method1---
method3---
method2---

再如下

public class Demo{

    public synchronized void synsMethod1(){
        System.out.println("method1---");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        synsMethod3();
    }
    public synchronized static void  synsMethod2(){
        System.out.println("method2---");
    }

    public synchronized  void  synsMethod3(){
        System.out.println("method3---");
    }
    public static void main(String[] args) {
        Demo demo=new Demo();
        new Thread(new Runnable() {
            @Override
            public void run() {
                demo.synsMethod1();
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                demo.synsMethod2();
            }
        }).start();
    }
}

输出结果(其中一种可能)

method1---
method2---
method3---

所以说静态方法的锁和非静态方法的锁是不一样的

Synchronized锁重入

关键字Synchronized拥有锁重入的功能,也就是在使用Synchronized的时候, 当一个线程得到一个对象的锁后,在该锁里执行代码的时候可以再次请求该对象的锁 时可以再次得到该对象的锁。

当线程请求一个由其它线程持有的对象锁时,该线程会阻塞,而当线程请求由自己持有的对象锁时,如果该锁是重入锁,请求就会成功,否则阻塞。

一个简单的例子就是:在一个Synchronized修饰的方法或代码块的内部调用本 类的其他Synchronized修饰的方法或代码块时,是永远可以得到锁的。

/**
 * @program: demo
 * @description:
 * @author: lee
 * @create: 2019-02-25
 **/
public class Demo{

    public synchronized void synsMethod1(){
        System.out.println("method1---");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        synsMethod2();
    }
    public synchronized void synsMethod2(){
        System.out.println("method2---");
    }
    public static void main(String[] args) {
        Demo demo=new Demo();
        new Thread(new Runnable() {
            @Override
            public void run() {
                demo.synsMethod1();
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                demo.synsMethod2();
            }
        }).start();
    }
}

为什么要引入可重入锁这种机制哪?

假如有一个线程T获得了对象A的锁,那么该线程T如果在未释放前再次请求该对象的锁时,如果没有可重入锁的机制,是不会获取到锁的,这样的话就会出现死锁的情况,所以最大的作用是避免死锁。

volatile与synchronized的

volatile关键字的作用就是强制从公共堆栈中取得变量的值,而不是线程私有的数据栈中取得变量的值。

关键字volatile是线程同步的轻量级实现,性能比synchronized要好,并且volatile只能修饰变量,而synchronized可以修饰方法,代码块等。

多线程访问volatile不会发生阻塞,而synchronized会发生阻塞。

volatile可以保证数据的可见性,但不可以保证原子性(不是线程安全的),而synchronized可以保证原子性,也可以间接保证可见性,因为他会将私有内存和公共内存中的数据做同步。

volatile解决的是变量在多个线程之间的可见性,而synchronized解决的是多个线程之间访问资源的同步性。

ThreadLocal

ThreadLocal提供了线程的局部变量,每个线程都可以通过set()和get()来对这个局部变量进行操作,但不会和其他线程的局部变量进行冲突,实现了线程的数据隔离。

简要言之:往ThreadLocal中填充的变量属于当前线程,该变量对其他线程而言是隔离的。

最典型的应用

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
 
/**
 * 采用ThreadLocal封装Connection
 * 
 * @author Administrator
 *
 */
public class ConnectionManager {
 
	//定义ThreadLocal静态变量,确定存取类型为Connection
	private static ThreadLocal<Connection> connectionHolder = new ThreadLocal<Connection>();
	
	/**
	 * 得到Connection
	 * @return
	 */
	public static Connection getConnection() {
		Connection conn = connectionHolder.get();
		//如果在当前线程中没有绑定相应的Connection
		if (conn == null) {
			try {
				Class.forName("oracle.jdbc.driver.OracleDriver");
				String url = "jdbc:oracle:thin:@localhost:1521:bjpowern";
				String username = "drp1";
				String password = "drp1";
				conn = DriverManager.getConnection(url, username, password);
				//将Connection设置到ThreadLocal
				connectionHolder.set(conn);
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		return conn;
	}
	
	/**
	 * 关闭数据库连接方法
	 * @return
	 */
	public static void closeConnection() {
		Connection conn = connectionHolder.get();
		if (conn != null) {
			try {
				conn.close();
				//从ThreadLocal中清除Connection
				connectionHolder.remove();
			} catch (SQLException e) {
				e.printStackTrace();
			}	
		}
	}
	
	/**
	 * 关闭数据库连接方法
	 * @return
	 */
	public static void close(Connection conn) {
		if (conn != null) {
			try {
				conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}
	
	public static void close(Statement pstmt) {
		if (pstmt != null) {
			try {
				pstmt.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}
	
	public static void close(ResultSet rs ) {
		if (rs != null) {
			try {
				rs.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}
	
	/**
	 * 事务开启
	 * @return
	 */
	public static void beginTransaction(Connection conn) {
		try {
			if (conn != null) {
				if (conn.getAutoCommit()) {
					conn.setAutoCommit(false); //手动提交
				}
			}
		}catch(SQLException e) {}
	}
	
	/**
	 * 事务提交
	 * @return
	 */
	public static void commitTransaction(Connection conn) {
		try {
			if (conn != null) {
				if (!conn.getAutoCommit()) {
					conn.commit();
				}
			}
		}catch(SQLException e) {}
	}
	
	/**
	 * 事务回滚
	 * @return
	 */
	public static void rollbackTransaction(Connection conn) {
		try {
			if (conn != null) {
				if (!conn.getAutoCommit()) {
					conn.rollback();
				}
			}
		}catch(SQLException e) {}
	}	
}

初始化默认值

public class ThreadlLocalDemo {

    /**
     * 初始化一个ThreadLocal对象,并且初始值设置为0
     */
    private static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>() {
        @Override
        protected Integer initialValue() {
            return 0;
        }
    };

    public int getNextNumber() {
        threadLocal.set(threadLocal.get() + 1);
        return threadLocal.get();
    }

    static class DemoThread extends Thread {
        ThreadlLocalDemo threadlLocalDemo = null;

        public DemoThread(ThreadlLocalDemo localDemo) {
            this.threadlLocalDemo = localDemo;
        }


        @Override
        public void run() {

            for (int i = 0; i < 5; i++) { // 每个线程打印 5个值
                System.out.println("Thread:" + Thread.currentThread().getName()
                        + ",threadlLocalDemo:" + threadlLocalDemo.getNextNumber());
            }

        }
    }

    public static void main(String[] args) {
        ThreadlLocalDemo threadlLocalDemo = new ThreadlLocalDemo();
        DemoThread demoThread = new DemoThread(threadlLocalDemo);
        DemoThread demoThread2 = new DemoThread(threadlLocalDemo);
        DemoThread demoThread3 = new DemoThread(threadlLocalDemo);
        DemoThread demoThread4 = new DemoThread(threadlLocalDemo);

        demoThread.start();
        demoThread2.start();
        demoThread3.start();
        demoThread4.start();

    }
}

ThreadLocal实现的原理

首先,我们来看一下ThreadLocal的set()方法,因为我们一般使用都是new完对象,就往里边set对象了

public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

上面有个ThreadLocalMap,我们去看看这是什么?


static class ThreadLocalMap {

        /**
         * The entries in this hash map extend WeakReference, using
         * its main ref field as the key (which is always a
         * ThreadLocal object).  Note that null keys (i.e. entry.get()
         * == null) mean that the key is no longer referenced, so the
         * entry can be expunged from table.  Such entries are referred to
         * as "stale entries" in the code that follows.
         */
        static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }
		
		 /**
	 * Construct a new map initially containing (firstKey, firstValue).
	 * ThreadLocalMaps are constructed lazily, so we only create
	 * one when we have at least one entry to put in it.
	 */
	 ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
		 table = new Entry[INITIAL_CAPACITY];
		 int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
		 table[i] = new Entry(firstKey, firstValue);
		 size = 1;
		 setThreshold(INITIAL_CAPACITY);
	 }
		//....很长
}

通过上面我们可以发现的是ThreadLocalMap是ThreadLocal的一个内部类。用Entry类来进行存储

我们的值都是存储到这个Map上的,key是当前ThreadLocal对象!

如果该Map不存在,则初始化一个:

void createMap(Thread t, T firstValue) {
     t.threadLocals = new ThreadLocalMap(this, firstValue);
}

如果map存在


/**
* Get the map associated with a ThreadLocal. Overridden in
* InheritableThreadLocal.
*
* @param  t the current thread
* @return the map
*/
ThreadLocalMap getMap(Thread t) {
	return t.threadLocals;
}

Thread维护了ThreadLocalMap变量


/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null

从上面又可以看出,ThreadLocalMap是在ThreadLocal中使用内部类来编写的,但对象的引用是在Thread中! 于是我们可以总结出:Thread为每个线程维护了ThreadLocalMap这么一个Map,而ThreadLocalMap的key是LocalThread对象本身,value则是要存储的对象。

有了上面的基础,我们看get()方法就一点都不难理解了:


public T get() {
	Thread t = Thread.currentThread();
	ThreadLocalMap map = getMap(t);
	if (map != null) {
		ThreadLocalMap.Entry e = map.getEntry(this);
		if (e != null) {
			@SuppressWarnings("unchecked")
			T result = (T)e.value;
			return result;
		}
	}
	return setInitialValue();
}

ThreadLocal原理总结

每个Thread维护着一个ThreadLocalMap的引用

ThreadLocalMap是ThreadLocal的内部类,用Entry来进行存储

调用ThreadLocal的set()方法时,实际上就是往ThreadLocalMap设置值,key是ThreadLocal对象,值是传递进来的对象

调用ThreadLocal的get()方法时,实际上就是往ThreadLocalMap获取值,key是ThreadLocal对象

ThreadLocal本身并不存储值,它只是作为一个key来让线程从ThreadLocalMap获取value。


695856371Web网页设计师②群 | 喜欢本站的朋友可以收藏本站,或者加入我们大家一起来交流技术!

0条评论

Loading...


发表评论

电子邮件地址不会被公开。 必填项已用*标注

自定义皮肤 主体内容背景
打开支付宝扫码付款购买视频教程
遇到问题联系客服QQ:419400980
注册梁钟霖个人博客